TL;DR
fullhashはビルド単位でハッシュ値を計算
chunkhashはエントリーポイント単位でハッシュ値を計算
contenthashはファイル単位でハッシュ値を計算
ハッシュの種類
webpackがビルドした後の出力ファイル名にハッシュを付与する場合、いくつかのハッシュ付与方法が存在する
- hash/fullhash
- chunkhash
- contenthash
hash/fullhash
hashはdeprecatedになっているので、fullhashを使用するのが良い。
この方法によるハッシュはビルド単位で作成される。
例
frontend
└── javascripts
└── packs
├── application.js
└── test.js
const path = require("path");
module.exports = {
context: path.resolve(__dirname),
entry: {
application: "./frontend/javascripts/packs/application.js",
test: "./frontend/javascripts/packs/test.js",
},
output: {
path: path.resolve(__dirname, "public/packs"), // output <absolute path>/public/packs
filename: "[name]-[fullhash].js",
chunkFilename: "[name].bundle-[fullhash].js", // [hash] is deprecated
publicPath: "/packs/",
},
};
この状態で npx webpack
によりビルドすると、
public/
├── packs
│ ├── application-c49cafe8d5713d24946e.js
│ └── test-c49cafe8d5713d24946e.js
上記のように 2つのエントリーポイントに対して同じハッシュが付与される。
片方のエントリーポイントしか修正していないのに、もう片方のハッシュも変更されるので、キャッシュの面で不利になる。
chunkhash
fullhashの場合は、あるチャンク内(あるエントリーポイント内で使用されるファイル群)のファイルが変更されたら他のチャンクのハッシュも変更されていた。
chunkhashの場合は、変更されたファイルを含んでいるチャンクのみハッシュ値が変更される。
例
console.log("hello Webpack!");
console.log("test.js");
❯❯❯ npx webpack
asset application-d75b5929b37355efec43.js 1.39 KiB [emitted] [immutable] (name: application)
asset test-00d31fb47a5466fd5acf.js 1.35 KiB [emitted] [immutable] (name: test)
./frontend/javascripts/packs/application.js 31 bytes [built] [code generated]
./frontend/javascripts/packs/test.js 30 bytes [built] [code generated]
hash値は以下
application.js -> d75b5929b37355efec43
test.js -> 00d31fb47a5466fd5acf
console.log("hello Webpack!");
// このファイルのみ変更(application.jsとは違うエントリーポイント)
console.log("chunk hash!!");
❯❯❯ npx webpack
assets by status 1.39 KiB [cached] 1 asset
asset test-9d106a1d904530f7c882.js 1.34 KiB [emitted] [immutable] (name: test)
./frontend/javascripts/packs/application.js 31 bytes [built] [code generated]
./frontend/javascripts/packs/test.js 28 bytes [built] [code generated]
webpack 5.73.0 compiled successfully in 82 ms
test.js のみハッシュ値が変更されているのがわかる。
次に、
test.jsからimportするファイルを作成してみる
import hoge from "./hoge.js";
hoge();
console.log("chunk hash!");
export default () => {
console.log("hoge.js");
};
npx webpack
assets by status 1.39 KiB [cached] 1 asset
asset test-22ad79f1d4a9552d9dd0.js 4.46 KiB [emitted] [immutable] (name: test)
runtime modules 670 bytes 3 modules
cacheable modules 149 bytes
./frontend/javascripts/packs/application.js 31 bytes [built] [code generated]
./frontend/javascripts/packs/test.js 66 bytes [built] [code generated]
./frontend/javascripts/packs/hoge.js 52 bytes [built] [code generated]
webpack 5.73.0 compiled successfully in 141 ms
test.js のハッシュ値は 22ad79f1d4a9552d9dd0
次に、test.js本体ではなく、test.jsからimportしているhoge.jsの内容を変更してみる。
export default () => {
console.log("modified!");
};
npx webpack
assets by status 1.39 KiB [cached] 1 asset
asset test-c0c483b67ef7151c01ba.js 4.48 KiB [emitted] [immutable] (name: test)
runtime modules 670 bytes 3 modules
cacheable modules 166 bytes
./frontend/javascripts/packs/application.js 31 bytes [built] [code generated]
./frontend/javascripts/packs/test.js 66 bytes [built] [code generated]
./frontend/javascripts/packs/hoge.js 69 bytes [built] [code generated]
test.js のハッシュ値が c0c483b67ef7151c01ba に変わっている。
よって chunkhash の場合は、 entrypointとなるjsファイルだけでなく、entrypointからimportしているファイルの変更に対してもハッシュ値の変更が行われる。
cssを絡ませてcontenthashとchunkhashの挙動の違いを探る
chunkhash
const path = require("path");
module.exports = {
mode: "development",
context: path.resolve(__dirname),
entry: {
application: "./frontend/javascripts/packs/application.js",
},
output: {
path: path.resolve(__dirname, "public/packs"), // output <absolute path>/public/packs
filename: "[name]-fiilename-[chunkhash].js",
chunkFilename: "[name].chunkFilename-[chunkhash].js", // [hash] is deprecated
publicPath: "/packs/",
},
module: {
rules: [
{
test: /\.css$/,
// 配列の最後尾から順に実行される。
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name]-[chunkhash].css", // <- この chunkhashに注目
}),
],
};
❯❯❯ npx webpack
asset application-fiilename-9b7d9841aa7d632e513f.js 5.12 KiB [emitted] [immutable] (name: application)
asset application-9b7d9841aa7d632e513f.css 310 bytes [emitted] [immutable] (name: application)
Entrypoint application 5.42 KiB = application-9b7d9841aa7d632e513f.css 310 bytes application-fiilename-9b7d9841aa7d632e513f.js 5.12 KiB
orphan modules 2.78 KiB (javascript) 937 bytes (runtime) [orphan] 7 modules
runtime modules 670 bytes 3 modules
cacheable modules 194 bytes (javascript) 28 bytes (css/mini-extract)
modules by path ./frontend/javascripts/packs/*.js 144 bytes
./frontend/javascripts/packs/application.js 90 bytes [built] [code generated]
./frontend/javascripts/packs/hoge.js 54 bytes [built] [code generated]
modules by path ./frontend/javascripts/packs/*.css 50 bytes (javascript) 28 bytes (css/mini-extract)
./frontend/javascripts/packs/index.css 50 bytes [built] [code generated]
css ./node_modules/css-loader/dist/cjs.js!./frontend/javascripts/packs/index.css 28 bytes [built] [code generated]
webpack 5.73.0 compiled successfully in 455 ms
application-fiilename-9b7d9841aa7d632e513f.js
application-9b7d9841aa7d632e513f.css
でhashが一緒。
contenthash
plugins: [
new MiniCssExtractPlugin({
filename: "[name]-[chunkhash].css", // <- ここだけ contenthashに変更
}),
],
❯❯❯ npx webpack
asset application-fiilename-fd0333cc80317b8b2be1.js 5.12 KiB [emitted] [immutable] (name: application)
asset application-1d6012e7893a7eca9678.css 310 bytes [emitted] [immutable] (name: application)
Entrypoint application 5.42 KiB = application-1d6012e7893a7eca9678.css 310 bytes application-fiilename-fd0333cc80317b8b2be1.js 5.12 KiB
orphan modules 2.78 KiB (javascript) 937 bytes (runtime) [orphan] 7 modules
runtime modules 670 bytes 3 modules
cacheable modules 187 bytes (javascript) 28 bytes (css/mini-extract)
modules by path ./frontend/javascripts/packs/*.js 137 bytes
./frontend/javascripts/packs/application.js 90 bytes [built] [code generated]
./frontend/javascripts/packs/hoge.js 47 bytes [built] [code generated]
modules by path ./frontend/javascripts/packs/*.css 50 bytes (javascript) 28 bytes (css/mini-extract)
./frontend/javascripts/packs/index.css 50 bytes [built] [code generated]
css ./node_modules/css-loader/dist/cjs.js!./frontend/javascripts/packs/index.css 28 bytes [built] [code generated]
webpack 5.73.0 compiled successfully in 477 ms
application-fiilename-fd0333cc80317b8b2be1.js
application-1d6012e7893a7eca9678.css
ハッシュが異なっている!
jsファイルを変更してみる。
export default () => {
console.log("updated only js file!");
};
❯❯❯ npx webpack
assets by status 310 bytes [cached] 1 asset
asset application-fiilename-195324247dbe25d9fa84.js 5.13 KiB [emitted] [immutable] (name: application)
Entrypoint application 5.44 KiB = application-1d6012e7893a7eca9678.css 310 bytes application-fiilename-195324247dbe25d9fa84.js 5.13 KiB
orphan modules 2.78 KiB (javascript) 937 bytes (runtime) [orphan] 7 modules
runtime modules 670 bytes 3 modules
cacheable modules 206 bytes (javascript) 28 bytes (css/mini-extract)
modules by path ./frontend/javascripts/packs/*.js 156 bytes
./frontend/javascripts/packs/application.js 90 bytes [built] [code generated]
./frontend/javascripts/packs/hoge.js 66 bytes [built] [code generated]
modules by path ./frontend/javascripts/packs/*.css 50 bytes (javascript) 28 bytes (css/mini-extract)
./frontend/javascripts/packs/index.css 50 bytes [built] [code generated]
css ./node_modules/css-loader/dist/cjs.js!./frontend/javascripts/packs/index.css 28 bytes [built] [code generated]
webpack 5.73.0 compiled successfully in 450 ms
application-fiilename-195324247dbe25d9fa84.js
application-1d6012e7893a7eca9678.css
jsファイルのhashだけ変更されていて、cssのhashは変更されていない!!
結論、 MiniCssExtractPlugin
のように別ファイルとしてbundle fileを出力したい場合、 chunkhashだと他のファイルが変更されてもhash値が変わってしまう。
今回の例でいうとcssを修正していないのに、jsファイルが変更されたことでcssのハッシュ値、ひいてはファイル名が異なることでブラウザのキャッシュが効かなくなり余計なリロードが発生してしまう。
そこで、別ファイルとして出力したいファイルに対しては、contenthashを指定してあげることで他のファイルの変更によって、自身のハッシュ値が変更されることがなくなる。
参考記事
https://webpack.js.org/configuration/output/#outputfilename
https://webpack.js.org/plugins/mini-css-extract-plugin/