Webpack疑问系列之hash/chunkhash/contenthash区别

1,824 阅读4分钟

定义

首先看一下官方的定义

  1. hash: 代表的是根据*compilation*过程计算的hash,其中compilation代表整个webpack的compiler编译过程,这些过程包括(打包文件内容、webpack配置等)

  2. chunkhash: 代表的是根据chunk内容计算的hash,chunk在webpack中表示最终打包的输出文件

  3. contenthash:指根据文件内容计算得来的hash

示例

后面的示例都会根据下面的项目结构和webpack配置进行修改和演示

  1. 代码结构
--demo4//项目入口文件
	--index.js
	--style.css
--package.json
--dist-demo4//打包目录
--webpack.config.js
  1. webpack.config.js
const path = require('path')
const CleanWebpackPlugin = require('clean-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin');// 用于抽离css文件,暂时可不管

module.exports = {
    entry: {
        index1: './demo4/index.js',
    },
    output: {
        filename: '[name].[hash:8].js',
        path: path.resolve(__dirname, 'dist-demo4')
    },
  	module: {
        rules: [
            {test: /\.css$/,
             use: [{loader: MiniCssExtractPlugin.loader},'css-loader']}// 用于抽离css文件,暂时可不管
        ]
    },
   plugins: [
        new CleanWebpackPlugin(),
     		new MiniCssExtractPlugin({
            filename: '[name].[contenthash:8].css',
	          chunkFilename: '[id].css'
        }),
    ]
}
  1. index.js
console.log('chunk')
  1. style.css
a {font-size: 12px;}

hash

根据上述定义,当在整个编译过程中一切都没改变时,所已算的hash是不会变的;但是代码变动或者webpack配置变动都会引起hash的计算变动

代码改变

  1. 首先我们执行编译npm start记录第一次计算的hash,得到hash值为d768a965
              Asset      Size  Chunks             Chunk Names
 index1.d768a965.js  1.02 KiB       0  [emitted]  index1
  1. 修改index.js代码并执行npm start查看新的hash值f070105d
// index.js
console.log('chunk changed')
// 编译
index1.f070105d.css  43 bytes       0  [emitted]  index1

配置更改

  1. 首先将webpack.config.js的配置做一点修改,编译之后得到hash值e43fce73
// webpack.config.js:修改插件MiniCssExtractPlugin.chunkFilename
chunkFilename: '[name].[id].css'//增加了[name].
//编译
index1.e43fce73.js  1.01 KiB       0  [emitted]  index1

注意: 配置更改必须要影响到整个编译过程其hash才会变动,单纯的在webpack.config.js中修改一些无意义代码的话hash是不会变动的

  1. 简单修改webpack.config.js并执行编译得到hash仍为e43fce73
// 在webpack.config.js中增加一个console
console.log('the compiler will not change')
// 编译
 index1.e43fce73.js  1.01 KiB       0  [emitted]  index1

chunkhash

根据定义得知chunkhash是根据打包的整个chunk内容计算hash,当整个chunk引入任意一个文件发生变动时,编译的chunk都会修改;

  1. 首先修改对应的webpack.config.js配置文件,将output的filename修改为filename: '[name].[chunkhash:8].js',*;然后在index.js中引入style.css
// wwebpack.config.js修改
output: {
        filename: '[name].[chunkhash:8].js',
        path: path.resolve(__dirname, 'dist-demo4'),
}
// index.js
import './style.css'

console.log('chunk changed')   
  1. 编译打包得到第一次参考的chunkhash为87319c9f
index1.87319c9f.css  43 bytes       0  [emitted]  index1
 index1.87319c9f.js  1.01 KiB       0  [emitted]  index1
  1. 修改index.js的到chunkhash为: 266332c3
index1.266332c3.css  21 bytes       0  [emitted]  index1
 index1.266332c3.js  1.01 KiB       0  [emitted]  index1
  1. 修改style.cssa {font-size: 14px;}得到chunkhash为:e84f56fd
index1.e84f56fd.css  21 bytes       0  [emitted]  index1
 index1.e84f56fd.js  1.01 KiB       0  [emitted]  index1

注意到上面打包的js和css的chunkhash值都是一样的,但是这里我们的css文件已经通过插件mini-css-extract-plugin从chunk中抽离出来了,因此我们可能希望打包的css拥有和chunk不一样的hash;此时我们就可以使用contenthash了

contenthash

根据定义contenthash时根据文件内容计算具体的hash值

因此当我们使用mini-css-extract-plugin将css抽离成一个单独文件时,可将该css的名称配置为filename: '[name].[contenthash:8].css',同时也将output打包出的chunk配置成contenthash,这样当css内容变动时,chunk的contenthash不会变动;chunk中非css文件的修改也不会影响到css的contenthash计算

webpack.config.js修改

output: {
  	filename: '[name].[contenthash:8].js',
    path: path.resolve(__dirname, 'dist-demo4'),
},
plugins: [
  	new CleanWebpackPlugin(),
  	new MiniCssExtractPlugin({
    		filename: '[name].[contenthash:8].css',
    		chunkFilename: '[name].[id].css'
  	}),
]
  1. 首先拿到第一次编译的contenthash值,js: ae51424c css: d8b9fd9f
 index1.ae51424c.js  1.01 KiB       0  [emitted]  index1
index1.d8b9fd9f.css  21 bytes       0  [emitted]  index1
  1. 修改index.js不修改style.css得到,js: 7a941304 css: d8b9fd9f;可以看到css的contenthash并没有变动
 index1.7a941304.js  1.01 KiB       0  [emitted]  index1
index1.d8b9fd9f.css  21 bytes       0  [emitted]  index1
  1. 修改style.css不修改index.js得到,js: 7a941304 css: 00583d11;可以看到此时js的contenthash没有变动,只有css文件的contenthash变动
index1.00583d11.css  21 bytes       0  [emitted]  index1
 index1.7a941304.js  1.01 KiB       0  [emitted]  index1

webpack官方文档

应用场景

当你的网站需要考虑长效缓存,可以使用不同的hash

  1. 当没有将css从chunk中抽离时直接使用chunkhash
  2. 当使用mini-css-extract-plugin插件抽离css时[1],可将chunk和css块都使用contenthash替换达到互不影响的作用
  3. 不要使用hash,这可能因为webpack的修改导致hash值变动而使缓存失效

关于hash的使用场景,可考虑将其作为版本控制;如将webpack.config.js的output.path更改为path.resolve(__dirname, 'dist-demo4', '[hash:6]'),这样就会将每次编译的版本保存为一个新目录;其他的场景暂时也想不到,欢迎大家分享:cowboy_hat_face:

脚注


  1. webpack官方文档推荐使用extract-text-webpack-plugin抽离css,但是实际上在webpack4中因为部分api的废除导致extract-text-webpack-plugin不再支持了,可以参考该插件的github介绍 ↩︎