Webpack hashの種類

# TL;DR
fullhashはビルド単位でハッシュ値を計算
chunkhashはエントリーポイント単位でハッシュ値を計算
contenthashはファイル単位でハッシュ値を計算

# ハッシュの種類
webpackがビルドした後の出力ファイル名にハッシュを付与する場合、いくつかのハッシュ付与方法が存在する

- hash/fullhash
- chunkhash
- contenthash


## hash/fullhash
hashはdeprecatedになっているので、fullhashを使用するのが良い。

この方法によるハッシュはビルド単位で作成される。

例

```
frontend
└── javascripts
    └── packs
        ├── application.js
        └── test.js
```

```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
```
上記のように ２つのエントリーポイントに対して同じハッシュが付与される。

片方のエントリーポイントしか修正していないのに、もう片方のハッシュも変更されるので、キャッシュの面で不利になる。

## chunkhash
fullhashの場合は、あるチャンク内（あるエントリーポイント内で使用されるファイル群）のファイルが変更されたら他のチャンクのハッシュも変更されていた。
chunkhashの場合は、変更されたファイルを含んでいるチャンクのみハッシュ値が変更される。

例
```javascript:application.js
console.log("hello Webpack!");
```

```javascript:test.js
console.log("test.js");
```

```shell
❯❯❯ 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

```javascript:application.js
console.log("hello Webpack!");
```

```javascript:test.js
// このファイルのみ変更（application.jsとは違うエントリーポイント）
console.log("chunk hash!!");
```

```shell
❯❯❯ 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するファイルを作成してみる

```javascript:test.js
import hoge from "./hoge.js";
hoge();
console.log("chunk hash!");
```

```javascript:hoge.js
export default () => {
  console.log("hoge.js");
};
```

```shell
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の内容を変更してみる。

```javascript:hoge.js
export default () => {
  console.log("modified!");
};
```

```shell
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

```javascript:webpack.config.js
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に注目
    }),
  ],
};
```

```shell
❯❯❯ 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
```javascript:webpack.config.js
  plugins: [
    new MiniCssExtractPlugin({
      filename: "[name]-[chunkhash].css", // <- ここだけ　contenthashに変更
    }),
  ],
```

```shell
❯❯❯ 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ファイルを変更してみる。
```javascript:hoge.js
export default () => {
  console.log("updated only js file!");
};
```

```shell
❯❯❯ 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://medium.com/@sahilkkrazy/hash-vs-chunkhash-vs-contenthash-e94d38a32208

https://webpack.js.org/configuration/output/#outputfilename
https://webpack.js.org/plugins/mini-css-extract-plugin/