webpack 之 babel-loader 中 cache 实现原理

4,989 阅读4分钟

webpack 之 babel-loader 中 cache 实现原理

原文地址: github.com/AshoneA/ove…

在即将到来的 webpack5 中加入了持久缓存,官方宣传能将 16000 个模块组成的应用打包时间减少 98%, 可参考 Sean Larkin 演讲地址。不过 webpack5 还没正式发布,周边生态很多还没有跟上,如果你现在贸然升级 webpack5 的话,或许只会得到一大堆的报错然后在 webpack issue 中寻找如何解决报错。那有没有办法在 webpack 4 中实现持久缓存呢?其实还是可以实现的。很多 loader 中其实已经实现了持久缓存,比如我们常用的 babel-loader、terser-webpack-plugin等已经实现了持久缓存,让你一次编译,下一次享受到缓存带来的速度提升。其实在 loader 中实现缓存还是一个比较简单的功能,本文就以 babel-loader 为例,来解释下 babel-loader 是如何实现缓存的。

在 webpack 中基于万物皆模块的思想,所谓的 loader 其实就是把一个模块转换成另一种形式的模块,甚至可以简单的理解 webpack 中的 loader 其实就是对文本字符串的处理,给指定的 loader 一个 input,loader 还你一个 output,有点函数式编程的内味了。因此所谓的 babel-loader 其实就是把一段 JavaScript 字符串转换成另一端 JavaScript 字符串。基于以上的前提,因此只要在转换的时候,记录下转换前的文件和转换后的文件,然后对比文件是否有改动,如果文件没有改动那就继续拿上次转换之后的文件,所以就跳过这一次转换的过程,大大提高了速度。

babel-loader 中可使用 cacheDirectory 来配置开启缓存,默认是关闭缓存的。下面我们从 babel-loader cache 源码中来看下 babel-loader 是如何处理的。具体的代码实现可参考:github.com/babel/babel…

// babel-loader 中使用的校验文件
// https://github.com/babel/babel-loader/blob/master/src/cache.js#L63
const filename = function(source, identifier, options) {
  const hash = crypto.createHash("md4");

  const contents = JSON.stringify({ source, options, identifier });

  hash.update(contents);

  return hash.digest("hex") + ".json";
};

从上面代码中可以看到 babel-loader 对 sourceidentifieroptionsJSON.stringify 之后做一次哈希作为文件名,以 json 文件的形式保存在指定目录中。具体的我们来讲下sourceidentifieroptions 代表什么含义。

所谓的 source 其实很好理解,也就是代码源文件。options 也很好理解,就是配置 babel-loader 的时候传给他的配置。还有个 identifier ,具体是做什么用的呢。想象下面一种情况,假如我们升级了 babel,那我们其实不能再继续使用原先的缓存,我们应该使之前的缓存失效。identifier 就是用来做这么一个标志,默认使用 @babel/core 的版本号,所以如果你升级了 babel 之后原先的缓存其实都是会失效,不过也提供了配置 identifier 的方式来根据业务具体配置。

如果三个变量一致的话其实我们就确定了两个文件是一样的, 所以问题就变成了如何高效的验证前后两次文件是否一致,从上面 babel-loader 源码中我们可以看到 babel-loader 中使用 md4 对文件做哈希来验证文件前后是否一致。其实最早开始的时候是使用 SHA-1 算法来做校验,不过其实校验文件前后是否一致其实并不需要到那么高强度的算法(SHA-1 使用 80 轮 160 位,md4 使用 3 轮 128 位,具体可参考 wiki ),SHA-1 实际上比 md4 慢的多,所以在一次 pr 中改成了 md4 校验。

总得来说 babel-loader 中的 cache 实现还是比较简单的,作为一个 loader 其实不用考虑太多的东西,还有 cache-loader 之类的实现,不过原理差不多,主要就是校验文件是否一致。不过作为 webpack 他自身要实现缓存的话得考虑不少东西,包括 webpack 配置啊,多缓存啊,如何让这个校验缓存的过程更加的高效,可以等 webpack 5 发布之后我们再来正式聊聊 webpack 5 的缓存。