babel-loader 和 ESLint 都是「逐文件处理」的重活:Babel 要解析、转译每个 JS 文件,ESLint 要对每个文件做静态分析。项目一大,它们往往成为构建时间的主要来源。好在两者都提供了缓存机制——没改动的文件,直接用上次的结果。本文讲清它们各自怎么配、怎么工作,以及和 webpack 自身缓存的关系。
一、为什么需要缓存
一次全量构建里,Babel 和 ESLint 都会处理所有匹配的文件。但开发中改动的通常只有一两个文件,其余几百个和上次一模一样,完全没必要重新处理。
缓存的核心思路是:
对「文件内容 + 相关配置」算一个哈希 → 作为 key
key 命中缓存 → 直接读取上次结果,跳过处理
key 未命中 → 正常处理,并把结果写入缓存
这样就实现了「只处理变了的文件」。它和增量编译不同:缓存是跨构建、甚至跨进程持久化的。
二、Babel 的缓存
1. 开启方式
babel-loader 通过 cacheDirectory 选项开启缓存:
module.exports = {
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
},
},
},
],
},
};
cacheDirectory: true:使用默认缓存目录,通常是node_modules/.cache/babel-loader。cacheDirectory: '/path/to/cache':自定义目录,便于 CI 环境统一缓存。- 默认关闭(
false),所以必须显式开启。
2. 相关选项
{
loader: 'babel-loader',
options: {
cacheDirectory: true,
cacheIdentifier: 'v1', // 手动加入缓存标识,改动它可强制失效
cacheCompression: true, // 缓存内容是否 gzip 压缩,默认 true
},
}
- cacheIdentifier:一个字符串,参与哈希计算。升级 Babel、调整配置后如果担心缓存不失效,可以手动改它来强制重建。
- cacheCompression:默认开启,压缩缓存文件能省磁盘,但读写时会多一次解压开销;磁盘充裕、追求速度可设为
false。
3. 原理与失效
babel-loader 的缓存 key 大致由三部分构成:
- 文件内容;
- Babel 相关配置(
.babelrc、babel.config.js内容、NODE_ENV、BABEL_ENV等); - 版本信息(
babel-loader、@babel/core的版本)。
任意一项变化,key 就变了,缓存自动失效并重新处理。所以正常情况下不需要手动清缓存。
注意:像
browserslist、tsconfig.json这类外部配置,未必都包含在默认的 cacheIdentifier 里。修改它们之后如果发现行为没变化,可以删掉node_modules/.cache/babel-loader再构建。
三、ESLint 的缓存
ESLint 在 webpack 5 中通过 eslint-webpack-plugin 集成(旧的 eslint-loader 已废弃)。它内置了 cache 选项:
const ESLintPlugin = require('eslint-webpack-plugin');
module.exports = {
plugins: [
new ESLintPlugin({
extensions: ['js', 'jsx', 'ts', 'tsx'],
cache: true,
}),
],
};
cache: false(默认):关闭缓存。cache: true:开启缓存,等价于cache: 'metadata'。cache: 'content':按文件内容判断是否变化。
缓存位置与策略
new ESLintPlugin({
cache: 'content',
cacheLocation: 'node_modules/.cache/eslint-webpack-plugin',
cacheStrategy: 'content', // 'metadata'(默认)| 'content'
})
两种策略的区别:
| 策略 | 判断依据 | 特点 |
|---|---|---|
metadata(默认) | 文件的 mtime + 大小 | 快,但对「改了内容却保持同样大小和时间」不敏感 |
content | 文件内容哈希 | 更准确,适合 CI 等 mtime 不可靠的环境 |
一般来说本地开发用默认的 metadata 就够;如果发现改了文件 ESLint 却没重新检查,或者运行在 CI 上,建议改成 content。
四、和 webpack 自身缓存的关系
webpack 5 内置的 cache: { type: 'filesystem' } 是更上层的缓存——它缓存的是整个模块的处理结果,Babel 转译、ESLint 检查的结果也可能被包含其中:
module.exports = {
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename],
},
},
};
两者的关系可以这样理解:
| loader/插件自带缓存 | webpack filesystem cache | |
|---|---|---|
| 缓存对象 | 单个工具的处理结果 | 模块及其整条 loader 链结果 |
| 作用范围 | Babel / ESLint 等各自维护 | 整个构建过程 |
| 典型场景 | 独立运行工具时也生效 | 主要加速 webpack 重复构建 |
实践上建议:
- 两个都开。webpack 的文件系统缓存负责整体提速,Babel/ESLint 的缓存则让对应工具本身更快,尤其在单独跑
eslint命令时也能受益。 - 如果磁盘或配置复杂度有限,至少开启
cache: { type: 'filesystem' },它往往已经能覆盖大部分收益。
五、完整配置示例
const path = require('path');
const ESLintPlugin = require('eslint-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].js',
},
cache: {
type: 'filesystem',
cacheDirectory: path.resolve(__dirname, 'node_modules/.cache/webpack'),
buildDependencies: {
config: [__filename],
},
},
module: {
rules: [
{
test: /\.m?jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
cacheCompression: false,
presets: ['@babel/preset-env', '@babel/preset-react'],
},
},
},
],
},
plugins: [
new ESLintPlugin({
extensions: ['js', 'jsx'],
cache: 'content',
cacheLocation: path.resolve(__dirname, 'node_modules/.cache/eslint-webpack-plugin'),
fix: true,
}),
],
};
六、常见问题
- 缓存没生效:确认
cacheDirectory/cache显式开启了,且缓存目录可写。 - 改了配置但结果没变:部分外部配置(browserslist、tsconfig 等)可能不在缓存 key 内。删掉
node_modules/.cache下对应目录,或调整cacheIdentifier。 - CI 上要不要缓存:可以,但要注意缓存跨机器失效的问题。优先用基于内容的策略(
cache: 'content'),或在缓存 key 中加入锁文件(package-lock.json)的哈希。 - 缓存目录别提交到 Git:
node_modules/.cache本就是缓存,应在.gitignore中忽略。
小结
| 工具 | 开启方式 | 默认目录 |
|---|---|---|
| Babel | babel-loader 的 cacheDirectory: true | node_modules/.cache/babel-loader |
| ESLint | eslint-webpack-plugin 的 cache: true / 'content' | node_modules/.cache/eslint-webpack-plugin |
| webpack | cache: { type: 'filesystem' } | node_modules/.cache/webpack |
一句话:Babel 缓存用 cacheDirectory,ESLint 缓存用 cache,再叠加 webpack 的 filesystem cache,三者一起开,构建速度的提升最明显。 遇到「配置改了却不生效」时,先清 node_modules/.cache 排查缓存问题。