Webpack 构建提速实战:Cache-loader 使用详解与优化技巧

a'ゞ彩云 优化 阅读 1,944
赞 14 收藏
二维码
手机扫码查看
反馈

我的写法,亲测靠谱

Cache-loader 这个东西,我最早是在一个 Vue 项目里用上的。当时项目越来越大,每次改一行代码,Webpack 就得重新跑一遍 Babel、TS、Vue 模板编译,动不动就等十几秒。后来翻文档看到 cache-loader,想着试试看,结果一加就真香了——热更新从 12 秒降到 3 秒,开发体验直接拉满。

Webpack 构建提速实战:Cache-loader 使用详解与优化技巧

但别急着 copy 别人的配置,我一开始就是这么干的,结果踩了好几个坑。现在我一般这样写:

// webpack.config.js(简化版)
const path = require('path');

module.exports = {
  module: {
    rules: [
      {
        test: /.js$/,
        use: [
          'cache-loader',
          {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true // 这个也开,双重保险
            }
          }
        ],
        include: path.resolve(__dirname, 'src')
      },
      {
        test: /.vue$/,
        use: [
          'cache-loader',
          'vue-loader'
        ]
      }
    ]
  }
};

关键点有几个:第一,cache-loader 必须放在最前面,因为 Webpack 的 loader 是从右往左执行的,你得先缓存后处理,不然缓存的是原始文件,没意义。第二,只对 src 目录生效,别把 node_modules 也包进去,不仅浪费空间,还可能因为依赖版本变化导致缓存污染。第三,Babel 自带的 cacheDirectory 也建议打开,和 cache-loader 不冲突,反而能互补。

缓存目录默认在 node_modules/.cache/cache-loader,不用管它,但如果你用的是 CI/CD,记得在构建前清掉缓存,不然可能因为环境差异导致奇怪的 bug。本地开发就完全不用管,让它自己跑就行。

这几种错误写法,别再踩坑了

我见过太多人把 cache-loader 用错,结果不但没提速,反而搞出一堆诡异问题。下面这几个反面案例,都是我或者同事亲手踩过的。

  • 把 cache-loader 放在最后:比如写成 ['babel-loader', 'cache-loader']。这等于用 Babel 处理完的代码再去缓存,但下次进来的时候,cache-loader 会直接返回缓存结果,跳过 Babel,那你的新代码根本没被编译!结果就是语法错误或者 undefined 变量满天飞。
  • 全局开启,不加 include/exclude:有人图省事,直接给所有 .js 文件加 cache-loader,连 node_modules 里的都缓存。结果某次升级 lodash,缓存没失效,Webpack 还在用旧版本的代码,调试半天才发现是缓存惹的祸。所以一定要用 include: /src/ 锁定范围。
  • 和 thread-loader 混用顺序错乱:有些项目为了提速,会同时用 thread-loader 和 cache-loader。正确的顺序应该是 ['cache-loader', 'thread-loader', 'babel-loader']。如果写成 ['thread-loader', 'cache-loader', 'babel-loader'],那 cache-loader 其实缓存的是 thread-loader 的中间产物,而 thread-loader 本身是多线程的,序列化/反序列化成本高,反而拖慢速度。
  • 在生产构建里用 cache-loader:千万别!cache-loader 是为开发环境设计的,目的是加速重复编译。生产构建本来就是一次性的,加它只会增加 I/O 开销,毫无收益。我之前接手一个项目,构建脚本里居然保留了 cache-loader,CI 跑得特别慢,删掉后快了 20%。

实际项目中的坑

除了配置顺序,还有些细节容易忽略。比如缓存键(cache key)的生成逻辑。cache-loader 默认会根据 loader 配置、文件内容、Webpack 版本等生成 hash。但如果你的 loader 配置里有函数或者动态值,比如:

{
  loader: 'some-loader',
  options: {
    customFn: () => { /* ... */ }
  }
}

那每次启动 Webpack,这个函数引用都会变,导致 cache key 不同,缓存永远失效。所以 options 里尽量只放纯对象或字符串。

另外,不要指望 cache-loader 能解决所有性能问题。它只对“重复编译相同文件”有效。如果你改的是公共组件,触发大量模块重新编译,那 cache-loader 帮不上忙,这时候得考虑 splitChunks 或者优化组件粒度。我之前有个页面,引入了 50 个图标组件,每次改一个图标,整个页面都要重编译,后来我把图标做成动态 import,配合 cache-loader,才真正稳住。

还有个小问题:cache-loader 在 Windows 上偶尔会因为路径大小写或者文件锁导致缓存写入失败。表现就是控制台报 EPERM 错误,但不影响构建。这种情况下,删掉 node_modules/.cache 重启就行,不算大问题,但得知道怎么回事,别慌。

最后提一句,Webpack 5 自带了持久化缓存(persistent caching),通过 cache: { type: 'filesystem' } 配置,效果比 cache-loader 更好,而且不用手动插 loader。如果你的项目已经升级到 Webpack 5,建议直接用内置缓存,别再折腾 cache-loader 了。不过很多老项目还在 Webpack 4,那就只能靠它续命。

结尾唠叨两句

以上是我这几年用 cache-loader 总结出来的一点经验,核心就一句话:**只在开发环境、只对源码、loader 顺序放对、别信万能药**。它确实能提升开发体验,但不是银弹。如果你的项目已经上 Webpack 5,赶紧切内置缓存;如果还在 Webpack 4,按我上面的写法配,基本不会出问题。

以上是我个人对 cache-loader 的完整讲解,有更优的实现方式欢迎评论区交流。这个技巧的拓展用法还有很多,后续会继续分享这类博客。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论