kd-cloud-web / Blog

一群人, 关于前端, 做一些有趣的事儿
13 stars 1 forks source link

webpack持久化缓存 #67

Open Luin-Li opened 2 years ago

Luin-Li commented 2 years ago

关于缓存这里不多说。本文要说的是,我们如何通过合理配置 webpack 去实现每次上线发布的静态资源 (CSS, JS, 图片) 的文件名称都是独一无二的。接下来主要回答两个问题:

  1. hash、chunkhash 和 contenthash 的区别

  2. 在 webpack 中如何固定 chunkhash

hash

hash模块识别码,用来处理模块之间的依赖关系。hash是跟每一次 webpack 打包的过程有关,任意增添或删减一个模块的依赖,hash值都会更改,并且全部文件都共用相同的hash值。

hash

为什么图片也是采用hash,值却和别人不一样呢? 因为 file-loader的hash字段是这个loader自己定义的占位符,和webpack的内置hash字段并不一致。

chunkhash

chunkhash根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值。只要我们不改动代码,就可以保证其哈希值不会受影响。

但是要保证chunkhash值在相关代码不变动的情况下也不变,还要做以下的事情:

  1. 提取运行时代码: 默认情况下,每个分离出来的chunk会包含webpack的runtime代码(用来解析和加载模块之类的运行时代码),所以即使该chunk没有改变,其他chunk改变了,chunkhash值也会改变,所以需要提取这部分代码即:
optimization: {
  runtimeChunk: {
    name: 'manifest'
  }
}
  1. 固定moduleId:模块id默认是按引入的顺序排序的,所以即使该文件没有变动,其他引入文件变动了,chunkhash可能也会变动,所以模块id的排序规则也要改变。development下默认采用路径的方式:
optimization: {
  namedModules: true
}

生产环境使用全路径是有点太长所以可以使用 HashedModuleIdsPlugin 插件来根据路径生成的 hash。

code

如上代码块,我们不改变 a.jsb.js 内的代码,只是调换两者引入的顺序,如果没有设置 namedModules, a.jsb.js的 chunkhash 值都会改变,打包效果对比如下图:

nameModules

  1. 固定chunkId:默认情况,生产环境下chunks ID 是以自增的数字命名,所以我们增加chunk 或者减少chunk的时候,也会导致顺序乱掉。所以,我们要固定chunkId:
optimization: {
  namedChunks: true // 默认情况下,开发环境为 true,生产环境为 false
}

chuckname

chunkId 与 moduleId 的区别 - 总结

Tips [chunkhash:8]类似这种写法是截取hash值的前8位,在生产环境不要这么做!!要取完整值[chunkhash]

这里有点小困惑,为什么默认情况下,开发环境为 true,生产环境为 false? 贴出git的解答

简单来说就是固定chunkId会导致:1)打包大小增加一点点;2)chuck名暴露

webpack@3中如果js和css文件都采用chunkhash,css文件改变,chunkhash值不会改变。只有js文件改变,chunkhash值才会改变。而在webpack@4中,不管是css文件还是js文件改变,chunkhash值都会改变。

但这都不是我们想要的结果,所以css文件的hash值应该采用contenthash,以区分 CSS 文件和 JS 文件的更新。

contenthash

contenthash根据当前文件的内容,来计算hash值。

那js文件可以用contenthash吗?可以,webpack 4.3+后。因为contenthash一开始只是被一些例如extract-text-webpack-plugin的插件使用,但是webpack 4.3+后,webpack自身也支持了。

总结:用webpack实现持久化缓存

webpack5

升级webpack5

chunkIds

webpack5-chunkid

开发环境optimization.chunkIds 会被设置成 named,但当在生产环境中时,它会被设置成 deterministic, 有利于长缓存。固定 chunkId 不再需要使用 import(/* webpackChunkName: "name" */ "module") ,从而避免在生产环境 chuck 名暴露。

chunk-build

chunk-build2

moduleIds

webpack5-moduleid

开发环境optimization.moduleIds 会被设置成 named,但当在生产环境中时,它会被设置成 deterministic

webpack5-moduleid2

webpack5-moduleid1

Webpack 4 如何优雅打包缓存文件

webpack 学习笔记之十一 hash chunkhash contenthash

优化(Optimization)