webpack 中 hash、chunkhash、contenthash 的区别

hash 一般是结合 CDN 缓存来使用,通过 webpack 构建之后,生成对应文件名自动带上对应的 MD5 值。如果文件内容发生改变的话,那么对应文件 hash 值也会改变,对应的 HTML 引用的 URL 地址也会改变,触发 CDN 服务器从原服务器上拉取对应数据,进而更新本地缓存。但是实际使用时,这三种 hash 计算还是有一定区别。

webpack 中对于输出文件名可以有三种 hash 值:

  1. hash

  2. chunkhash

  3. contenthash

这三者有什么区别呢?

hash

hash 是跟整个项目的构建相关,构建生成的文件 hash 值都是一样的,所以 hash 计算是跟整个项目的构建相关,同一次构建过程中生成的 hash 都是一样的,只要项目里有文件更改,整个项目构建的 hash 值都会更改。

如果出口是 hash,那么一旦针对项目中任何一个文件的修改,都会构建整个项目,重新获取 hash 值,缓存的目的将失效。

chunkhash

采用 hash 计算的话,每一次构建后生成的 hash 值都不一样,即使文件内容压根没有改变。这样子是没办法实现缓存效果,我们需要另一种 hash 值计算方法,即 chunkhash

chunkhash 根据不同的入口文件(Entry)进行依赖文件解析、构建对应的 chunk,生成对应的 hash 值。我们在生产环境里把一些公共库和程序入口文件区分开,单独打包构建,接着我们采用 chunkhash 的方式生成 hash 值,那么只要我们不改动公共库的代码,就可以保证其 hash 值不会受影响。

由于采用 chunkhash,所以项目主入口文件 main.js 及其对应的依赖文件 main.css 由于被打包在同一个模块,所以共用相同的 chunkhash,但是公共库由于是不同的模块,所以有单独的 chunkhash。这样子就保证了在线上构建时只要文件内容没有更改就不会重复构建。

1
entry: {
2
    main: path.join(__dirname,'./main.js'),
3
    vendor: ['vue']
4
},
5
output: {
6
    path:path.join(__dirname,'./dist'),
7
    publicPath: '/dist/',
8
    filname: 'bundle.[chunkhash].js'
9
}

最后 mainvendor 的打包结果图:

chunkhash

contenthash

contenthash 表示由文件内容产生的 hash 值,内容不同产生的 contenthash 值也不一样。在项目中,通常做法是把项目中css 都抽离出对应的 css 文件来加以引用。

在这里我用 mini-css-extract-plugin 替代了 extract-text-webpack-plugin

1
const miniCssExtractPlugin = require("mini-css-extract-plugin");
2
module.exports = {
3
    module: {
4
        rules: [
5
            {
6
                test: /\.css$/,
7
                use: [
8
                    miniCssExtractPlugin.loader,
9
                    'css-loader'
10
                ]
11
            }
12
        ]
13
    },
14
    plugins: [
15
        new miniExtractPlugin({
16
            filename: 'main.[contenthash:7].css'
17
        })
18
    }
19
}

打包结果如图:
contenthash

打包后即使 css 文件所处的模块里就算其他文件内容改变,只要 css 文件内容不变,那么就不会重复构建。

附加

如果对 css 使用了 chunkhash 之后,它与依赖它的 chunk 共用 chunkhash,测试后会发现,cssjs 文件名的 chunkhash 值是一样的,如果我修改了 js 文件,jshash 值会变化,css 的文件名的 hash 还是和变化后的 js 文件的 hash 值一样,如果我修改了 css 文件,也会导致重新构建,csshash 值和 jshash 值还是一样的,即使 js 文件没有被修改。这样会导致缓存作用失效,所以 css 文件最好使用 contenthash