以前有个错误认识:以为在webpack配置中,只要设置mode="production"即可以实现代码的压缩。不知道什么时候留下的这个错误印象,可怕。
本文结合splitChunks,记录一下本次对项目打包深度优化的过程。
为了方便讨论,新建了一个React + Antd项目:github.com/imnull/webp…
WebPack核心相关版本
"webpack": "^4.46.0",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.2"
因为webpack@^5的周边支持还不是很好,之前写插件发现改动有点大,所以还是使用webpack4。对应的webpack-cli和dev-server版本,也需要按照上面的配置,才能很好运行。
Build Level 0
除了配置了各种loader、plugin之外,没有对打包输出做任何额外配置。
$ npm run b0
Asset Size Chunks Chunk Names
client.eb3b.js 259 KiB 0 [emitted] [immutable] [big] client
index.html 134 bytes [emitted]
Entrypoint client [big] = client.eb3b.js
Entrypoints:
client (259 KiB)
client.eb3b.js
看一下产出物,JS代码确实做了压缩。难道是说非单包情况,需要额外增加代码压缩配置?
单个JS文件大小超过了244KiB,下面做一下分包。
Build Level 1 - splitChunks
增加了optimization项,其中splitChunks.cacheGroups配置如下:
cacheGroups: {
common: {
name: 'common',
chunks: 'initial',
priority: 100,
reuseExistingChunk: false,
enforce: true,
test: m => /\/node_modules\/(react|redux|classnames|prop-types|lodash|acorn|rc-)/.test(m.context),
},
antd: {
name: 'antd',
chunks: 'initial',
priority: 90,
reuseExistingChunk: true,
enforce: true,
test: m => /\/node_modules\/(antd|@ant-design)/.test(m.context),
},
}
这里分了两个包:common和antd。
需要注意的cacheGroups的几个特征:
- 依赖
test检测文件名称,对包分组进行筛选; - 按
priority优先级排序,数组越大优先级越高,优先打入高优先分组; - 剩余内容全部打入
entry中的包。 看下运行效果:
Asset Size Chunks Chunk Names
antd.3469.js 136 KiB 0 [emitted] [immutable] antd
client.f63a.js 62.7 KiB 1 [emitted] [immutable] client
common.15da.js 169 KiB 2 [emitted] [immutable] common
index.html 208 bytes [emitted]
Entrypoint client [big] = common.15da.js antd.3469.js client.f63a.js
Entrypoints:
client (368 KiB)
common.15da.js
antd.3469.js
client.f63a.js
体积果然膨胀了很多
PS: antd已配置为按需打包
Build Level 2 - UglifyJsPlugin
就像文章开头所说,看下产出物,代码格式整整齐齐。
引入uglifyjs-webpack-plugin来做JS压缩:
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
//...
optimization: {
minimizer: [
new UglifyJsPlugin(),
],
// ,,,
}
运行效果:
Asset Size Chunks Chunk Names
antd.8c2d.js 99.3 KiB 0 [emitted] [immutable] antd
client.ca88.js 23.8 KiB 1 [emitted] [immutable] client
common.fb46.js 136 KiB 2 [emitted] [immutable] common
index.html 208 bytes [emitted]
Entrypoint client [big] = common.fb46.js antd.8c2d.js client.ca88.js
Entrypoints:
client (259 KiB)
common.fb46.js
antd.8c2d.js
client.ca88.js
效果非常显著,减少了1/4多,和单包带压缩体积一致。
继续观察产出物,发现JS中内嵌的CSS代码还是未压缩的原始文本,带有换行和空格:
//...
r.push([e.i,".container {\n border: 3px solid #aaa;\n padding: 20px;\n width: 350px;\n margin: 0 auto;\n display: flex;\n flex-direction: column;\n align-items: center;\n}\n\n.container h1 {\n margin: 0;\n padding: 0;\n line-height: 2em;\n}",""])
///...
Build Level 3 - splitChunks剥离样式文件
在splitChunks.cacheGroups中增加一项:
styles: {
name: 'styles',
test: /\.(scss|css|less)$/,
chunks: 'all',
priority: 110,
enforce: true
},
注意优先级priority是当前最高的:
Asset Size Chunks Chunk Names
antd.06a8.js 23.2 KiB 0 [emitted] [immutable] antd
client.e11d.js 23.4 KiB 1 [emitted] [immutable] client
common.33d5.js 136 KiB 2 [emitted] [immutable] common
index.html 246 bytes [emitted]
styles.848c.js 76.7 KiB 3 [emitted] [immutable] styles
Entrypoint client [big] = styles.848c.js common.33d5.js antd.06a8.js client.e11d.js
Entrypoints:
client (259 KiB)
styles.848c.js
common.33d5.js
antd.06a8.js
client.e11d.js
能看出来,client和antd两个包都有减小,是因为其中的样式文件都归并到了styles分包中。
但体积总量依然是259KiB,查看styles分组内容,发现内嵌样式文本依然是未压缩的。
所以说,虽然该方案通过分包降低了单位体积,但并未实质上实现CSS压缩。
Build Level 4 - MiniCss + OptimizeCss
用到两个工具:
- mini-css-extract-plugin
- optimize-css-assets-webpack-plugin
这个方案,是根据chunk分组进行CSS代码提取和压缩,并产生对应的CSS文件,与上面Level 3的styles分包配置冲突,所以需要先删掉styles分包配置。
配置loader
在配置中,原有css、less配置的第一项loader都是style-loader,替换为MiniCss.loader,
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
// ...
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader, // 这里
'css-loader',
],
exclude: /node_modules/,
},
{
test: /\.less$/i,
use: [
MiniCssExtractPlugin.loader, // 这里
'css-loader',
{
loader: 'less-loader',
options: {
lessOptions: {
javascriptEnabled: true,
}
},
}
],
}
]
}
}
配置optimization
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
// ...
module.exports = {
optimization: {
minimizer: [
new MiniCssExtractPlugin({
filename: '[name].[contenthash:4].css'
}),
new OptimizeCssAssetsPlugin({}),
// ...
]
}
}
运行效果
Asset Size Chunks Chunk Names
antd.7aee.js 23.2 KiB 0 [emitted] [immutable] antd
antd.cd7e.css 56.3 KiB 0 [emitted] [immutable] antd
client.3ea8.js 20.4 KiB 1 [emitted] [immutable] client
client.7dc3.css 175 bytes 1 [emitted] [immutable] client
common.fd2a.js 136 KiB 2 [emitted] [immutable] common
index.html 298 bytes [emitted]
Entrypoint client = common.fd2a.js antd.cd7e.css antd.7aee.js client.7dc3.css client.3ea8.js
在webpack-bundle-analyzer中显示的结果(不包含CSS文件)为:
All (179.61 KB)
common.fd2a.js (135.98 KB)
antd.7aee.js (23.22 KB)
client.3ea8.js (20.41 KB)
目录简介显示总体积为242,052字节,转换为KiB,最终体积约为236.38KiB。
本次优化结束,以上。