Webpack 操练场 ②:使用 Webpack 构建 TypeScript 开发环境

使用 Webpack 构建 TypeScript 开发环境

对应代码:https://github.com/EsunR/webpack-playground/tree/main/templates/html-ts

准备 TypeScript 环境

首先安装 TypeScript:

1
pnpm install typescript -D

然后生成 tsconfig.json 配置文件:

1
2
3
4
pnpm exec tsc --init

# npm
npx tsc --init

这时候根目录就会生成 tsconfig.json 文件,这个文件是 TypeScript 的配置文件,我们可以在这个文件中配置 TypeScript 的编译选项,我们调整如下几个选项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"compilerOptions": {
"target": "ES5", // 将代码转为 ES5 语法
"module": "CommonJS", // 使用 CommonJS 规范
"moduleResolution": "node10", // 模块解析方式,不配置在引用模块时如果不是完整路径会报错
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}, // 配置路径别名,主要让 vscode 识别,跟 webpack.config.js 中的 alias 保持对应
"allowJs": true, // 允许编译 js 文件
"outDir": "./dist" // 编译产出,我们使用 webpack 不会根据这里的配置走,但是如果不配置 tsconfig 会报错
// 其余配置保持默认不改动
}
}

之后我们将 webpack.config.js 修改为 webpack.config.ts,并将代码规范修改为 ESM,这样就可以编写有 TypeScript 提示的 webpack 配置了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import path from 'path';
// 引入 webpack 的类型
import type { Configuration as WebpackConfiguration } from 'webpack';
// 保证配置 devServer 时不会报类型错误
import 'webpack-dev-server';

const isDev = process.env.NODE_ENV !== 'production';

// 通过 TypeScript 我们可以添加类型声明
const config: WebpackConfiguration = {
mode: isDev ? 'development' : 'production',
devtool: isDev ? 'eval-cheap-module-source-map' : 'source-map',
entry: path.resolve(__dirname, './src/main.ts'),
output: {
path: path.resolve(__dirname, './dist'),
clean: true,
},
// 其他 webpack 配置
};

export default config;

然后修改启动脚本:

1
2
3
4
5
6
7
8
{
"scripts": {
- "dev": "cross-env NODE_ENV=development webpack serve --config webpack.config.js",
+ "dev": "cross-env NODE_ENV=development webpack serve --config webpack.config.ts",
- "build": "cross-env NODE_ENV=production webpack --config webpack.config.js"
+ "build": "cross-env NODE_ENV=production webpack --config webpack.config.ts"
}
}

执行构建指令后会报错,这是因为我们只安装了 typescript 环境,但是如果想让 webpack 执行 TypeScript 的配置文件还需要 TypeScript 的运行执行指令,因此我们需要安装 ts-node,ts-node 可以让我们在直接运行 ts 代码而不需要编译成 js:

1
pnpm install ts-node -D

之后,webpack 就可以成功执行 TypeScript 编写的 webpack 配置文件了。

添加 ts-loader

准备完环境后,我们将 js 代码修改为 ts 代码后,webpack 会报错,这是因为 webpack 默认只能处理 js 代码,如果要处理 ts 代码,需要添加对应的 loader,这里我们使用 ts-loader 来处理 ts 代码:

1
pnpm install ts-loader -D

webpack.config.ts 中添加对应的 loader,同时要让 webpack 支持 ts 模块的解析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const config: WebpackConfiguration = {
// ... 其他配置
module: {
rules: [
// ... 其他 loader ...
// 处理 ts 文件
{
test: /\.tsx?$/i,
use: [
{
loader: 'ts-loader',
options: {
// 指定特 tsconfig 的位置,也可以不指定,默认使用项目根目录的 tsconfig.json
configFile: path.resolve(__dirname, './tsconfig.json'),
},
},
],
exclude: /node_modules/,
},
],
},
resolve: {
alias: {
// ... ...
},
// 将 .ts (如果有需要也要加入 .tsx) 文件添加到解析列表中,否则在 import ts 模块时,如果不带文件后缀就会报错
extensions: ['.js', '.ts', '.tsx'],
},
};

除了 ts—loader,因为项目中使用了 babel,还可以使用 babel-loader 结合 @babel/preset-typescript 来处理 ts,但是这样不支持类型检查,这里不再做演示。

另外,如果追求编译速度,可以使用 esbuild 或者使用 swc 替换 babel。

处理静态资源模块

当引用静态资源时,ts 会报类型错误。

这是因为 ts 无法识别我们导入的静态资源模块,通过编写类型声明文件可以解决这个问题。在 src 目录下创建 types 文件夹,并新建 static.d.ts 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
declare module '*.css' {
const classes: { readonly [key: string]: string };
export default classes;
}

declare module '*.png' {
const url: string;
export default url;
}

declare module '*.jpg' {
const url: string;
export default url;
}

declare module '*.jpeg' {
const url: string;
export default url;
}

declare module '*.gif' {
const url: string;
export default url;
}

declare module '*.svg' {
const url: string;
export default url;
}

declare module '*.webp' {
const url: string;
export default url;
}

这样,引入静态资源模块时,ts 就不会报类型错误的问题了。

ts-loader 结合 babel-loader

参考:《在 Webpack 中同时使用 ts-loader 和 babel-loader》