环境配置
基础配置
概要
- ts-node
- tsc
- t s config.json
- vscode
- 和 webpack 搭配使用
ts-node
Node 环境的 typescript 解释执行器。REPL(Read eval print loop)。
npm i -g ts-node
# yarn global add ts-node
bash
使用 ts-node 执行文件
ts-node smoe.ts
bash
配置文件
tsconfig.json
{
"compilerOptions": {}
}
json
tsc
一个 ts 的编译器。
npm i -g tsc
# yarn global add tsc
bash
可以指定编译某个 ts 文件:
tsc hello.ts
bash
也可以通过 tsconfig.json 配置。
- 可以用 outDir 配置项配置 js 文件输出的位置;
- tsc 作为一个指令,可以用 --help 查看用法;
- 可以用 module 指定生成模块的类型。
当存在 tsconfig.json 时,可以在项目根目录下可以直接运行 tsc 命令对文件进行编译。
{
"compilerOptoins": {
"outDir": "dist"
}
}
json
与 webpack 搭配使用
初始化项目
mkdir ts-webpack
cd ts-webpack
npm init -y
bash
安装依赖
npm install webpack webpack-cli ts-loader typescript --save-dev
# yarn add webpack webpack-cli ts-loader typescript
bash
编写测试文件
// src/index.ts
export class TreeNode<T> {
left: TreeNode<T>
right: TreeNode<T>
data: T
constructor(data: T) {
this.data = data
}
}
function log(x) {
console.log(x)
}
const node = new TreeNode<number>(100)
log(node.data)
typescript
编写 webpack 配置文件
// webpack.config.js
const path = require('path')
module.exports = {
entry: {
index: './src/index.ts'
},
mode: 'development',
module: {
rules: [
{
test: /\.ts/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
output: {
filename: 'bundle.[name].js',
path: path.resolve(__dirname, 'dist')
}
}
js
编写 npm 脚本
{
"name": "ts-webpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start:dev": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"ts-loader": "^9.4.2",
"typescript": "^4.9.5",
"webpack": "^5.75.0",
"webpack-cli": "^5.0.1"
}
}
json
运行测试脚本
yarn start:dev
node dist/bundle.index.js
bash
进阶配置
概要
- react + ts-loader 配置
- react + babel 配置
- vue + vue-loader 配置
- vue + babel 配置
核心问题:preset 的顺序有关系吗?preset 的作用是什么?
react + ts-loader
安装 npm 包
pnpm i react react-dom
bash
pnpm i @types/react @types/react-dom --save-dev
bash
pnpm i awesome-typescript-loader --save-dev
bash
ts-loader 已经安装过,这里不再安装。
pnpm i webpack-dev-server html-webpack-plugin -D
bash
编写 react 文件
// src/ReactHello.tsx
import React from 'react'
import ReactDOM from 'react-dom'
const App: () => JSX.Element = () => {
return (
<div>
<h1>Hello React!</h1>
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
编写 tsconfig.json 文件
{
"compilerOptions": {
"esModuleInterop": true, // 支持多套编码风格
"jsx": "react" // react, div => React.createElement
}
}
json
编写 template 模板文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
</head>
<body>
<div id="root"></div>
</body>
</html>
html
编写 webpack.react.js 文件
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: {
index: './src/ReactHello.tsx'
},
mode: 'development',
module: {
rules: [
{
test: /\.tsx?/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
output: {
filename: 'bundle.[name].js',
path: path.resolve(__dirname, 'dist')
},
devServer: {
static: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'template.html')
})
]
}
js
编写测试脚本
"scripts": {
"start:dev": "webpack",
"start:react": "webpack --config webpack.react.js"
}
bash
运行测试脚本
yarn start:react
bash
这样运行命令并不会启动开发服务器,我们需要修改 scripts 脚本。
"scripts": {
"start:dev": "webpack",
"start:react": "webpack server --config webpack.react.js"
}
bash
重新运行脚本,就可以看到开发服务器被启动,可以正常访问。
react + babel preset
babel-preset 和 ts-loader 的区别是什么?
-
babel 作用
- The Compiler for next generation Javascript
- 所有编译 JS 的事情,babel 都干
- es6 => es5 => es3 => polyfill
-
缓存 + 优化
-
插件 + 生态
两者本质区别不大,不过从生态和优化上来看,babel 更优。
babel-loader 背后是 babel,ts-loader 背后是 tsc 编译器。
pnpm i babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript --save-dev
bash
Babel 有两套机制,一套是 preset,一套是 plugin。我们可以认为 preset 是 plugin 的集合,其实就是预选项的概念。
编写 webpack.react.withbabel.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// ...
module: {
rules: [
{
test: /\.tsx?/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-typescript',
'@babel/preset-react', // 不使用 ts-loader 的情况下,需要使用
'@babel/preset-env'
]
}
},
exclude: /node_modules/
}
]
},
// ...
}
js
编写 scripts 脚本
{
"scripts": {
"start:dev": "webpack",
"start:react": "webpack server --config webpack.react.js",
"start:babel": "webpack server --config webpack.react.withbabel.js"
}
}
json
vue + loader
增加 vue 依赖
pnpm i vue@next -D
pnpm i @vue/compiler-sfc -D
pnpm i vue-loader -D
bash
sfc:Single File Component,单文件组件
编写 vue sfc 及 bootstraper
<template>
<h1>Hello Vue!</h1>
</template>
<script lang="ts">
export default {
setup() {
return {}
}
}
</script>
// src/main.ts
import { createApp } from 'vue'
import HelloVue from './VueHello.vue'
createApp(HelloVue).mount('#root')
typescript
// src/shims-vue.d.ts
/* eslint-disable */
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
typescript
vue 需要增加 shims-vue.d.ts 文件,否则 main.ts 中引入 .vue 文件会报错。
shim(垫片),通常为了处理兼容而命名。上面这个 shim 的目标是让 vscode 和 webpack 知道 .vue 的文件可以被当作一个组件定义文件来使用。
编写 webpack.vue.js 文件
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')
module.exports = {
entry: {
index: './src/main.ts'
},
mode: 'development',
module: {
rules: [
{
test: /\.tsx?/,
loader: 'ts-loader',
options: {
// 无法识别 vue 转换的 ts 文件,需要配置该规则添加后缀
appendTsSuffixTo: [/\.vue$/]
},
exclude: /node_modules/
},
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
output: {
filename: 'bundle.[name].js',
path: path.resolve(__dirname, 'dist')
},
devServer: {
static: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'template.html')
}),
new VueLoaderPlugin()
]
}
js
编写 script 脚本
{
"scripts": {
"start:dev": "webpack",
"start:react": "webpack server --config webpack.react.js",
"start:babel": "webpack server --config webpack.react.withbabel.js",
"start:vue": "webpack server --config webpack.vue.js"
}
}
json
vue + babel preset
安装依赖
pnpm i babel-preset-typescript-vue3 -D
bash
编写 webpack.vue.withbabel.js 文件
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')
module.exports = {
entry: {
index: './src/main.ts'
},
mode: 'development',
module: {
rules: [
{
test: /\.tsx?/,
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', 'babel-preset-typescript-vue3', '@babel/preset-typescript']
},
exclude: /node_modules/
},
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
output: {
filename: 'bundle.[name].js',
path: path.resolve(__dirname, 'dist')
},
devServer: {
static: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'template.html')
}),
new VueLoaderPlugin()
]
}
js
编写 script 脚本文件
{
"scripts": {
"start:dev": "webpack",
"start:react": "webpack server --config webpack.react.js",
"start:react-babel": "webpack server --config webpack.react.withbabel.js",
"start:vue": "webpack server --config webpack.vue.js",
"start:vue-babel": "webpack server --config webpack.vue.withbabel.js"
}
}
json
技术拓展
Babel preset 执行顺序有关系吗?
存在顺序关系,真正执行时可能存在多种执行顺序。
preset-react 和 preset-vue 的作用是什么?
@babel/preset-react:集成更多的 plugin,让我们可以使用,其实就是一个 plugin 包。
Preset-typescript-vue3 只增加了 typescript 的转换能力。
实战建议
实际开发时,还是推荐使用官方脚手架 react-create- app、vue 或者 vite。
我们学习环境配置只是作为架构师的一项基本技能。当你架构项目时,我们需要的是一个成熟的脚手架,推荐使用第三方的工具。