2、Webpack缓存机制:Webpack持久化缓存原理、cache配置项详解、缓存版本控制
聊到Webpack的缓存,我得先坦白一件事。
早些年我做项目,每次改一行代码都要等十几秒重新构建。那时候团队十几个人,每天光等构建的时间加起来,够看两集电视剧了。后来我深入研究了一下Webpack的缓存机制,才明白——原来大部分重复工作,根本没必要做第二遍。
持久化缓存:到底在缓存什么?
说白了,Webpack的持久化缓存就是把编译结果存到磁盘上。下次启动时,如果文件没变,就直接读缓存,跳过编译。
你想想看,一个项目少说几百个模块。每次构建都重新解析、转换、打包,太浪费了。缓存的核心思路就一句话:变了的重新编译,没变的直接复用。
Webpack 5之前,缓存主要靠内存,或者靠第三方loader的缓存。Webpack 5直接内置了持久化缓存,这才算真正解决了问题。
核心原理:Webpack会为每个模块生成一个「内容哈希」。构建时对比哈希值,如果一致,说明模块没变,直接从缓存读取编译结果。
这里有个细节——缓存的不只是编译后的代码。还包括模块的依赖关系图、解析结果、loader处理后的中间产物。说白了,整个构建过程中的中间状态,能缓存的都缓存了。
cache配置项详解
配置其实不复杂。我直接给你看最常用的写法:
// webpack.config.js
module.exports = {
cache: {
type: 'filesystem', // 启用文件系统缓存
cacheDirectory: path.resolve(__dirname, '.temp_cache'), // 缓存存放目录
buildDependencies: {
config: [__filename] // 配置文件变化时,缓存失效
},
version: '1.0', // 缓存版本号
name: 'production-cache', // 缓存名称,用于区分不同环境
store: 'pack', // 缓存存储方式
compression: 'gzip', // 缓存压缩方式
maxAge: 5184000000 // 缓存有效期,默认60天
}
};
我来拆开讲讲每个配置项的实际用途。
type: 'filesystem'
这是Webpack 5的核心配置。以前只有memory模式,构建完就没了。filesystem模式会把缓存写到磁盘,下次启动直接读。我个人习惯在开发环境用memory,生产环境用filesystem。不过现在磁盘读写够快,我基本全用filesystem了。
cacheDirectory
缓存放哪。默认是node_modules/.cache/webpack。我建议单独指定一个目录,比如.temp_cache。这样清理缓存时方便,直接删文件夹就行。
小技巧:把缓存目录加到.gitignore里。别问我为什么,问就是曾经有人把缓存提交到仓库,然后整个团队都炸了。
buildDependencies
这个配置很关键。它告诉Webpack:哪些文件的变化会导致整个缓存失效。最常见的就是webpack.config.js本身。你改了配置,之前的缓存肯定不能用了。
buildDependencies: {
config: [__filename],
// 还可以加其他依赖
// tsconfig: [path.resolve(__dirname, 'tsconfig.json')]
}
version
缓存版本号。手动控制缓存失效的利器。比如你升级了某个loader,或者改了babel配置,但Webpack不知道。这时候手动改一下version,所有缓存立即失效。
注意:version不是随便写的。我建议用语义化版本号,比如'1.0'、'1.1'。每次重大配置变更就升一位。我曾经见过有人用时间戳当version,结果每次构建都重新缓存,等于没开缓存。
name
缓存名称。主要用于区分不同构建环境。比如开发环境和生产环境的缓存不能混用。
name: process.env.NODE_ENV === 'production' ? 'prod-cache' : 'dev-cache'
store与compression
store默认是'pack',把缓存打包成单个文件。compression默认是'gzip',压缩缓存文件。这两个一般不用改。除非你有特殊需求,比如想用更快的压缩算法。
maxAge
缓存有效期。默认60天。过了这个时间,缓存自动失效。我一般不改这个,60天够长了。项目迭代快的话,一个月缓存就自动更新了。
缓存版本控制:别让缓存变成坑
缓存是好东西,但用不好就是坑。我来说说缓存版本控制的几个关键点。
什么时候缓存会失效?
- 文件内容变化:模块内容变了,该模块的缓存失效
- 配置文件变化:webpack.config.js变了,整个缓存失效
- 依赖变化:node_modules里的包变了,相关缓存失效
- 手动版本号变化:version变了,全部缓存失效
- 超过有效期:maxAge到了,缓存自动失效
我踩过的坑
曾经有一次,我升级了babel-loader,但忘了改version。结果构建出来的代码还是旧的。排查了半天才发现是缓存没失效。从那以后,我养成了一个习惯:每次升级loader或plugin,顺手改一下version。
最佳实践:在package.json里加一个脚本,专门用来清理缓存。
// package.json
{
"scripts": {
"clean:cache": "rm -rf .temp_cache node_modules/.cache"
}
}
多环境缓存管理
开发环境和生产环境的缓存一定要分开。不然你在开发环境调试的代码,被生产环境缓存了,上线后还是旧代码。
// 根据环境区分缓存
cache: {
type: 'filesystem',
name: process.env.NODE_ENV === 'production'
? 'prod-cache'
: 'dev-cache',
version: process.env.NODE_ENV === 'production'
? '1.0.0'
: '1.0.0-dev'
}
实战配置示例
最后给你一个我项目中实际在用的配置。嗯,这个配置经过多次迭代,目前比较稳定。
// webpack.cache.config.js
const path = require('path');
module.exports = {
cache: {
type: 'filesystem',
cacheDirectory: path.resolve(__dirname, '.webpack_cache'),
buildDependencies: {
config: [
__filename,
path.resolve(__dirname, 'babel.config.js'),
path.resolve(__dirname, 'tsconfig.json')
]
},
version: '2.1.0',
name: `app-${process.env.NODE_ENV}`,
store: 'pack',
compression: 'gzip',
maxAge: 1000 * 60 * 60 * 24 * 30, // 30天
hashAlgorithm: 'md4' // 哈希算法,默认md4
}
};
最后说一句:缓存不是银弹。如果你的项目构建本来就很快,开缓存反而多了一步读写磁盘的操作。我建议项目超过100个模块,或者构建时间超过5秒,再考虑开启持久化缓存。
好了,Webpack的缓存机制就聊到这。下一章我们聊聊热更新——那个让你改代码后页面自动刷新的神奇机制。