主题
构建性能--持久化缓存
原理
整个过程中存在许多 CPU 密集型操作,例如调用 Loader 链加载文件时,遇到 babel-loader、eslint-loader、ts-loader 等工具时可能需要重复生成 AST;分析模块依赖时则需要遍历 AST,执行大量运算;Seal 阶段也同样存在大量 AST 遍历,以及代码转换、优化操作,等等。假设业务项目中有 1000 个文件,则每次执行 npx webpack
命令时,都需要从 0 开始执行 1000 次构建、生成逻辑。
而 Webpack5 的持久化缓存功能则将构建结果保存到文件系统中,在下次编译时对比每一个文件的内容哈希或时间戳,未发生变化的文件跳过编译操作,直接使用缓存副本,减少重复计算;发生变更的模块则重新执行编译流程。
webpack5
设置 cache.type = 'filesystem'
即可开启:
js
module.exports = {
//...
cache: {
type: 'filesystem'
},
//...
}
此外,cache
还提供了若干用于配置缓存效果、缓存周期的配置项,包括:
cache.type
:缓存类型,支持'memory' | 'filesystem'
,需要设置为filesystem
才能开启持久缓存;cache.cacheDirectory
:缓存文件路径,默认为node_modules/.cache/webpack
;cache.buildDependencies
:额外的依赖文件,当这些文件内容发生变化时,缓存会完全失效而执行完整的编译构建,通常可设置为各种配置文件,如:
js
module.exports = {
cache: {
type: 'filesystem',
buildDependencies: {
config: [
path.join(__dirname, 'webpack.dll_config.js'),
path.join(__dirname, '.babelrc')
],
},
},
};
cache.managedPaths
:受控目录,Webpack 构建时会跳过新旧代码哈希值与时间戳的对比,直接使用缓存副本,默认值为['./node_modules']
;cache.profile
:是否输出缓存处理过程的详细日志,默认为false
;cache.maxAge
:缓存失效时间,默认值为5184000000
。
性能可以提升几十倍
webpack4
cache-loader
cache-loader
能够将 Loader 处理结果保存到硬盘,下次运行时若文件内容没有发生变化则直接返回缓存结果
- 安装依赖
shell
npm i -D cache-loader
- 配置,注意必须将
cache-loader
放在loader
数组首位
js
module.exports = {
// ...
module: {
rules: [{
test: /\.js$/,
use: ['cache-loader', 'babel-loader', 'eslint-loader']
}]
},
// ...
}
cache-loader
只缓存了 Loader 执行结果,缓存范围与精度不如 Webpack5 内置的缓存功能,所以性能效果相对较低。能提升稳定在 60% ~ 80% 之间
hard-source-webpack-plugin
它并不仅仅缓存了 Loader 运行结果,还保存了 Webpack 构建过程中许多中间数据,包括:模块、模块关系、模块 Resolve 结果、Chunks、Assets 等,效果几乎与 Webpack5 自带的 Cache 对齐。
- 安装依赖
shell
npm i -D hard-source-webpack-plugin
- 配置
js
const HardSourceWebpackPlugin = require("hard-source-webpack-plugin");
module.exports = {
// ...
plugins: [
new HardSourceWebpackPlugin(),
],
}
底层逻辑与 Webpack5 的持久化缓存很相似,但优化效果稍微差一些。性能提升稳定在 62% ~ 88% 之间
组件自带的缓存
- babel-loader
- eslint-loader:旧版本 ESLint Webpack 组件,官方推荐使用 eslint-webpack-plugin 代替
- eslint-webpack-plugin
- stylelint-webpack-plugin
例如使用 babel-loader
时,只需设置 cacheDirectory = true
即可开启缓存功能
js
module.exports = {
module: {
rules: [{
test: /\.m?js$/,
loader: 'babel-loader',
options: {
cacheDirectory: true,
},
}]
},
}
DLL
dll 在 webpack4 以下可以使用,从而提升性能,但 webpack4 及以上打包性能足够好,没有必要再使用了。
当成知识点了解即可
作用:事先把常用但又构建时间长的代码提前打包好(例如 react、react-dom),后面再打包的时候就跳过原来的未打包代码,直接使用
- 需要新建个构建配置文件,比如是
webpack.dll.config.js
js
module.exports = {
context: process.cwd(),
entry: {
library: [
'react',
'react-dom',
'redux',
'react-redux'
],
// 如果有多个,直接在增加一个
},
output: {
// 这里打包后的文字是 library.dll.js
filename: '[name].dll.js',
path: path.resolve(__dirname, 'build/libarary'),
// 暴露的库的名字
library: '[name]'
},
plugins: [
// 指定包存放的位置
new webpack.DllPlugin({
name: '[name]',
// 描述动态链接库 mainfest 文件输出时的文件名称
// path: 'manifest.json'
path: path.resolve(__dirname, 'build/libarary/[name].json')
})
]
}
- 在
pageage.json
scripts
中增加命令
json
"scripts": {
"dll": "webpack --config webpack.dll.js"
}
- 执行
npm run dll
分包 - 在
webpack.config.js
引入
js
module.exports = {
plugins: [
new webpack.DllReferencePlugin({
// 刚打包后的json文件地址
// manifest: require('xxx.json'),
manifest: require('./build/library/libary.json'),
// 指定需要用到的 manifest 文件,
// webpack 会根据这个 manifest 文件的信息,分析出哪些模块无需打包,直接从另外的文件暴露出来的内容中获取
})
// 如果引入多个,使用多次此插件
]
}
- 每次公共部分代码的变化,都需要重新执行分包指令
建议使用 autodll-webpack-dll 简化配置过程