记打包工具从 roadhog 迁移至 webpack

记录对公司项目的构建工具的改造,也可以说是升级。

项目背景

该项目是一个 React 的 PC 端 toB 后台项目。

项目由 5 年前的 antd-pro 搭建,主要使用了 React 16 + Antd 3 + roadhog2 (现 Umi 前身)。

为什么要做迁移

1.线上/本地构建速度慢

构建机器性能

  • build: 450s

本地

  • build: 300s+
  • dev 启动: 60.76s
  • dev 二次启动:同上
  • HMR: 53s

2.本地内存占用高

  • 本地 build: 3.1G+
  • 本地 dev: 1.8-2G+

3.roadhog 早已不维护,最后一次更新是2018年,后续维护和改动困难

目标是什么

1.尽可能不改动业务代码,不影响业务

2.构建性能提升

怎么做

1.改造技术选型:webpack5

为什么是 webpack5?

  1. 和其他构建工具相比:rollup 主要用于库的打包;vite 的开发模式和线上模式使用的构建方案不同,容易产生不一致
  2. webpack 是个比较成熟的方案,社区的踩坑和问题解决都比较丰富,webpack5内置了很多优化,配置要比之前简单
  3. roadhog 其实是基于 webpack 的封装,因此迁移到webpack5,也可以说是 webpack 版本升级,改动成本相比其他工具较小

2.观察原构建输出结构,与改造后对比

3.webpack 配置

实际改造中,体感上 webpack5 比之前的版本相比,优点在于:

  1. 许多配置内置了,无需关注
  2. 各种报错会比较友好,甚至会提供建议
  3. 根据 mode 选项不同的自带优化

以下贴出本人改造后的基础配置:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
module.exports = {
// 入口,该项目为SPA,只有一个入口
entry: './src/index.js',
// 输出
output: {
// 产物输出文件名
// hash、chunkhash、contenthash区别:https://medium.com/@web_developer/hash-vs-chunkhash-vs-contenthash-e94d38a32208
filename: '[name].[contenthash:8].js',
path: path.resolve(__dirname, 'dist'),
// 每次build清理输出目录
clean: true,
// 输出assets文件名,因为项目中的图片不会变化了,如logo,所以不进行hash了
assetModuleFilename: '[base]',
},

plugins: [
// 让产物被html模板引用,或者说时注入
new HtmlWebpackPlugin({
minify: false, // 是否压缩,这里为了方便确认比对注入是否正确关闭了
}),
// 将css单独导出成文件的插件,没有这个插件的话,css就需要被style-loader处理插入到dom中
new MiniCssExtractPlugin({
filename: '[name].[contenthash:8].css',
// FIX: WARNING in chunk styles [mini-css-extract-plugin] Conflicting order
ignoreOrder: true,
}),
// 将public素材复制到dist目录下,这个主要看前端部署方式和静态资源引用方式
new CopyPlugin({
patterns: [
{ from: 'public' },
],
}),
// 根据构建时的环境,替换项目中的环境变量为常量
new webpack.DefinePlugin({
'process.env.ENV': JSON.stringify('dev'),
'process.env.API_ENV': JSON.stringify('development'),
}),
// webpack打包进度条
new WebpackBar(),
],

// webpack处理文件的方式,配置各种loader,主要看项目的技术栈去查阅官网上loader相关的配置
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.css$/i,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
},
],
},
{
test: /\.less$/i,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
modules: true,
},
},
{ loader: 'less-loader',
options: {
lessOptions: {
// // https://github.com/ant-design/ant-motion/issues/44
javascriptEnabled: true,
},
},
},
],
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
generator: {
publicPath: 'static/',
outputPath: 'static/',
},
},
],
},

// 别名
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},

// 开发模式的本地服务
devServer: {
static: './dist',
// HMR
hot: true,
},
};

在此基础上,根据 dev 和 prod 两种模式,对配置进行拆分,拆分为 webpack.config.dev.js 和 webpack.config.prod.js,然后在 package.json 中配置不同的 script 命令,如:

1
2
3
4
5
6
{
"scripts": {
"dev": "webpack serve --config webpack.config.dev.js",
"build": "webpack --config webpack.config.prod.js"
}
}

4.验证

  1. 开启 dev 脚本,检查页面运行和报错情况
  2. 比对新旧 dist 产物是否缺失
  3. 部署测试,检查构建机器上是否正常运行与部署情况
  4. 测试环境验证

5.优化

以上步骤实现了我们的基本目标:迁移 webpack5,同时提升了构建的速度。目前还没有进行专门的优化,接下来看下是否能进一步优化构建速度。

webpack 官方文档中有一篇文章专门介绍来优化构建速度,链接

按照文章中的指导,进行了一些尝试:

1.使用持久化缓存 cache ✅

1
2
3
cache: {
type: 'filesystem',
},

增加缓存的方式让 dev 二次启动的时间降低至 1.1s, 是原来的1/15。

2.DLL ❌

网上搜了下相关资料,webpack5 中 cache 方案已经是 DLL 的上位替代方案了, 参考

实际上我加了 DLL 配置,构建时间大概提升 1s 左右

3.使用 esbuild-loader 编译js ❌

因为项目中使用了装饰器,但是esbuild 目前不支持 js 的装饰器,会打包失败

结果

构建机器性能提升 260%

打包时间降至 70s+, 提升 600%

  • build: 73s ~ 80s

本地开发启动时间提升 375%

本地构建时间降至 30s, 提升 1000%

本地开发首次启动时间降至 16s, 提升 375%,二次启动时间降至 1.1s,提升 60000%

  • build: 30s
  • dev 首次启动: 16s
  • dev 二次启动: 1.1s
  • HMR: 0.5 - 1s+
  • 内存占用: 500m+