webpack的hash、chunkhash、contenthash

对于webpack的hash,经常使用于cdn缓存。我理解的是文件不变的状况下,最后打包出来的hash串也不会变。最近被问到了这是三个hash的区别,就查了一下,发现还颇有讲究。css

先看一下三个hash的解释:react

  • [hash] is a "unique hash generated for every build"
  • [chunkhash] is "based on each chunks' content"
  • [contenthash] is "generated for extracted content"

这里有两次代码变动

原代码:webpack

// file1.js
console.log('file1')

// file2.js
console.log('file2')

// file3.js
console.log('file3')

// index.js
require('./file2')
console.log('index')

// file1.js
require('./file1')
console.log('detail')

// webpack.config.js
const path = require('path')
const webpack = require('webpack')

module.exports = {
  // mode: 'development',
  // mode: 'production',
  entry: {
    index: './src/index.js',
    detail: './src/detail.js',
  },
  output: {
    filename: '[name].[hash].js',
    path: path.resolve(__dirname, 'dist')
  },
}
复制代码

第一次变动:git

// file2.js
require('./file22')
复制代码

第二次变动:github

// index.js
require('./file2')
require('./file3')
console.log('index')
复制代码

下面我会以我理解的顺序比较一下三个hashweb

hash

每次构建的生成惟一的一个hash,且全部的文件hash串是同样的。 源代码构建: 缓存

image

第一次变动: app

image

是否是看到非预期的地方了?我只改了file2.js,index.js的hash串变了,可是为何detail.js为何也变了?这还怎么有缓存的做用!不行,升级!ide

chunkhash

每个文件最后的hash根据它引入的chunk决定ui

源代码构建:

image

第一次变动:

image
此次文件的hash变化符合预期,index的变了,detail的没变

第二次变动:

image
你会发现此次变动也是基于index的变动,可是实际上detail的文件内容没有变,那为何它的hash也跟着变了?

缘由是 module identifier,由于 index 新引入的模块改变了之后全部模块的 id 值,因此 detail 文件中引入的模块 id 值发生了改变,因而 detail 的 chunkhash 也随着发生改变。

不用怕,webpack已经提供方案了,解决方案是将默认的数字 id 命名规则换成路径的方式。webpack 4 中当 mode 为 development 会默认启动,可是production环境仍是默认的id方式,webpack也提供了相应的plugin来解决这个问题

plugins: [
    new webpack.HashedModuleIdsPlugin(),
],
复制代码

加上这个plugin后,再走一遍上述代码的变动,你会发现第一次、第二次的变动后,detail的hash串仍然没有变化,符合预期。

在webpack中,有css的状况下,每一个entry file会打包出来一个js文件和css文件,在使用chunkhash的状况下,js和css的文件的hash会是同样的,这个时候暴露出来的一个问题:你修一个react的bug,可是并无改样式,最后更新后,js和css的文件的hash都变了。这个仍是不太好,css文件的hash串不变最好,再继续升级!

contenthash

contenthash是根据抽取到的内容来生成hash。

生产环境是否是会使用一个MiniCssExtractPlugin来进行css的压缩,这个时候咱们在这个plugin里面指定hash为contenthash,你会发现修改js文件后,js文件的hash串变了,css的hash串没变!完美。

new MiniCssExtractPlugin({
  // Options similar to the same options in webpackOptions.output
  // both options are optional
  filename: '[name].[contenthash:8].css',
  chunkFilename: '[name].[contenthash:8].chunk.css'
})
复制代码

将webpack中的所有hash都设置成contenthash的状况下,仅仅只修改css文件,不改js文件的状况下,css文件的hash串会变,js文件的不会变,这样能达到最小更新。

image

image

image

其余

我查了两个流行的脚手架:create-react-app和umi,发现它们的entry file的配置都是contenthash

output: {
    filename: '[name].[contenthash].js',
    chunkFilename: 'cfn_[name].[contenthash].js',
  },
复制代码

umi使用了HashedModuleIdsPlugin来进行稳定的hash构建,可是cra没有,我看有人已经提issue了:github.com/facebook/cr…,做者说optimization.moduleIds: "hashed"这个也能知足需求,查了webpack5中optimization.moduleIds是能够的

因此目前最佳实践是contenthash+HashedModuleIdsPlugin/optimization.moduleIds: "hashed"

参考资料:

相关文章
相关标签/搜索