webpack之hash、chunkhash和contenthash

文件的hash值通常作为前端静态资源实现增量更新的方案之一,因为通过网络获取资源可能会很慢。 这也就是为什么浏览器需要缓存资源的原因。

如果我们在部署新版本时不更改资源的文件名,浏览器可能会认为它没有被更新,就会使用它的缓存版本。告诉浏览器下载较新版本的一种简单方法就是更改资源的文件名。

1.hash与chunkhash

首先我们先看一下官方文档对于两者的定义:

[hash] is replaced by the hash of the compilation.(hash代表的是compilation的hash值。)

[chunkhash] is replaced by the hash of the chunk.(chunkhash代表的是chunk(模块)的hash值。)

什么是 compilation?compilation对象代表某个版本的资源对应的编译进程。compilation在项目中任何一个文件改动后就会被重新创建,然后webpack计算新的compilation的hash值,这个hash值便是hashhash是compilation对象compiler①计算所得,而不是具体的项目文件计算所得。所以所有的文件名都会使用相同的hash指纹。

①compiler对象代表的是配置完备的Webpack环境。 compiler对象只在Webpack启动时构建一次,由Webpack组合所有的配置项构建生成。

例如使用hash配置产生了2个相同hash文件名的js文件:

1
2
3
4
5
6
output: {
filename: '[name].[hash:8].js',
path: __dirname + '/built'
}
//a.778asdef.js
//b.778asdef.js

这样带来的问题是,这2个js文件任何一个改动都会影响另外两个文件的最终文件名。上线后,另外两个文件的浏览器缓存也全部失效。这肯定不是我们想要的结果。

这时候就用到了chunkhash,chunkhash是根据具体模块文件的内容计算所得的hash值,所以某个文件的改动只会影响它本身的hash指纹,不会影响其他文件。配置webpack的output如下:

1
2
3
4
5
6
output: {
filename: '[name].[chunkhash:8].js',
path: __dirname + '/built'
}
//a.778asdfg.js
//b.778afrds.js

这样每个文件的hash都不相同,上线后无改动的文件不会失去缓存。

2. js与css共用相同chunkhash的解决方案

webpack的理念是把所有类型的文件都以js为汇聚点,不支持js文件以外的文件为编译入口。所以如果我们要编译style文件,唯一的办法是在js文件中引入style文件。如下:

1
import 'style/style.scss';

webpack默认将js/style文件统统编译到一个js文件中,可以借助extract-text-webpack-plugin将style文件单独编译输出。但是在计算chunkhash时,会把所有的js代码和style代码混合在一起计算。比如main.js引用了main.scss:

这时候,如果我们只是修改了css,那么不但单独编译出来的css文件hash值有改变,对应的JS文件hash也会改变。

这时候就用到了contenthash

extract-text-webpack-plugin提供了另外一种hash值:contenthash。顾名思义,contenthash代表的是文本文件内容的hash值,也就是只有style文件的hash值。这个hash值就是解决上述问题的银弹。修改配置如下:

1
new ExtractTextPlugin('[name].[contenthash].css');

这样编译输出的js和css文件将会有其独立的hash值。