创见博客
webpack 中 Babel 与 ESLint 的缓存
七崽爱吃小饼干2026/09/16阅读 0专栏 webpack构建高级

babel-loader 和 ESLint 都是「逐文件处理」的重活:Babel 要解析、转译每个 JS 文件,ESLint 要对每个文件做静态分析。项目一大,它们往往成为构建时间的主要来源。好在两者都提供了缓存机制——没改动的文件,直接用上次的结果。本文讲清它们各自怎么配、怎么工作,以及和 webpack 自身缓存的关系。

一、为什么需要缓存

一次全量构建里,Babel 和 ESLint 都会处理所有匹配的文件。但开发中改动的通常只有一两个文件,其余几百个和上次一模一样,完全没必要重新处理。

缓存的核心思路是:

对「文件内容 + 相关配置」算一个哈希 → 作为 key
key 命中缓存 → 直接读取上次结果,跳过处理
key 未命中 → 正常处理,并把结果写入缓存

这样就实现了「只处理变了的文件」。它和增量编译不同:缓存是跨构建、甚至跨进程持久化的。

二、Babel 的缓存

1. 开启方式

babel-loader 通过 cacheDirectory 选项开启缓存:

js
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. 相关选项

js
{
  loader: 'babel-loader',
  options: {
    cacheDirectory: true,
    cacheIdentifier: 'v1',       // 手动加入缓存标识,改动它可强制失效
    cacheCompression: true,      // 缓存内容是否 gzip 压缩,默认 true
  },
}
  • cacheIdentifier:一个字符串,参与哈希计算。升级 Babel、调整配置后如果担心缓存不失效,可以手动改它来强制重建。
  • cacheCompression:默认开启,压缩缓存文件能省磁盘,但读写时会多一次解压开销;磁盘充裕、追求速度可设为 false。

3. 原理与失效

babel-loader 的缓存 key 大致由三部分构成:

  1. 文件内容;
  2. Babel 相关配置(.babelrc、babel.config.js 内容、NODE_ENV、BABEL_ENV 等);
  3. 版本信息(babel-loader、@babel/core 的版本)。

任意一项变化,key 就变了,缓存自动失效并重新处理。所以正常情况下不需要手动清缓存。

注意:像 browserslist、tsconfig.json 这类外部配置,未必都包含在默认的 cacheIdentifier 里。修改它们之后如果发现行为没变化,可以删掉 node_modules/.cache/babel-loader 再构建。

三、ESLint 的缓存

ESLint 在 webpack 5 中通过 eslint-webpack-plugin 集成(旧的 eslint-loader 已废弃)。它内置了 cache 选项:

js
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':按文件内容判断是否变化。

缓存位置与策略

js
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 检查的结果也可能被包含其中:

js
module.exports = {
  cache: {
    type: 'filesystem',
    buildDependencies: {
      config: [__filename],
    },
  },
};

两者的关系可以这样理解:

loader/插件自带缓存webpack filesystem cache
缓存对象单个工具的处理结果模块及其整条 loader 链结果
作用范围Babel / ESLint 等各自维护整个构建过程
典型场景独立运行工具时也生效主要加速 webpack 重复构建

实践上建议:

  • 两个都开。webpack 的文件系统缓存负责整体提速,Babel/ESLint 的缓存则让对应工具本身更快,尤其在单独跑 eslint 命令时也能受益。
  • 如果磁盘或配置复杂度有限,至少开启 cache: { type: 'filesystem' },它往往已经能覆盖大部分收益。

五、完整配置示例

js
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,
    }),
  ],
};

六、常见问题

  1. 缓存没生效:确认 cacheDirectory / cache 显式开启了,且缓存目录可写。
  2. 改了配置但结果没变:部分外部配置(browserslist、tsconfig 等)可能不在缓存 key 内。删掉 node_modules/.cache 下对应目录,或调整 cacheIdentifier。
  3. CI 上要不要缓存:可以,但要注意缓存跨机器失效的问题。优先用基于内容的策略(cache: 'content'),或在缓存 key 中加入锁文件(package-lock.json)的哈希。
  4. 缓存目录别提交到 Git:node_modules/.cache 本就是缓存,应在 .gitignore 中忽略。

小结

工具开启方式默认目录
Babelbabel-loader 的 cacheDirectory: truenode_modules/.cache/babel-loader
ESLinteslint-webpack-plugin 的 cache: true / 'content'node_modules/.cache/eslint-webpack-plugin
webpackcache: { type: 'filesystem' }node_modules/.cache/webpack

一句话:Babel 缓存用 cacheDirectory,ESLint 缓存用 cache,再叠加 webpack 的 filesystem cache,三者一起开,构建速度的提升最明显。 遇到「配置改了却不生效」时,先清 node_modules/.cache 排查缓存问题。

评论
0/100