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"

參考資料:

相關文章
相關標籤/搜索