キャッシュ

したがって、webpackを使用してモジュール式のアプリケーションをバンドルし、展開可能な/distディレクトリを生成します。/distの内容がサーバーにデプロイされると、クライアント(通常はブラウザー)はそのサーバーにアクセスしてサイトとそのアセットを取得します。最後のステップは時間がかかる可能性があるため、ブラウザーはキャッシングと呼ばれる手法を使用します。これにより、サイトは不要なネットワークトラフィックを減らし、より高速にロードできます。ただし、新しいコードを取得する必要がある場合に頭痛の種になる可能性もあります。

このガイドでは、webpackコンパイルによって生成されたファイルが、内容が変更されない限りキャッシュされたままになるようにするために必要な設定に焦点を当てます。

出力ファイル名

output.filename置換設定を使用して、出力ファイルの名前を定義できます。webpackには、置換と呼ばれる角括弧で囲まれた文字列を使用してファイル名をテンプレート化する方法が用意されています。[contenthash]置換は、アセットの内容に基づいて一意のハッシュを追加します。アセットの内容が変更されると、[contenthash]も変更されます。

はじめにの例と、index.htmlファイルを手動で維持する必要がないように、出力管理pluginsを使用して、プロジェクトをセットアップしましょう。

プロジェクト

webpack-demo
|- package.json
|- package-lock.json
|- webpack.config.js
|- /dist
|- /src
  |- index.js
|- /node_modules

webpack.config.js

  const path = require('path');
  const HtmlWebpackPlugin = require('html-webpack-plugin');

  module.exports = {
    entry: './src/index.js',
    plugins: [
      new HtmlWebpackPlugin({
-       title: 'Output Management',
+       title: 'Caching',
      }),
    ],
    output: {
-     filename: 'bundle.js',
+     filename: '[name].[contenthash].js',
      path: path.resolve(__dirname, 'dist'),
      clean: true,
    },
  };

この設定でビルドスクリプトnpm run buildを実行すると、次の出力が生成されます

...
                       Asset       Size  Chunks                    Chunk Names
main.7e2c49a622975ebd9b7e.js     544 kB       0  [emitted]  [big]  main
                  index.html  197 bytes          [emitted]
...

ご覧のとおり、バンドルの名前は(ハッシュを介して)その内容を反映しています。変更を加えずに別のビルドを実行すると、ファイル名は同じままであることが予想されます。ただし、もう一度実行すると、そうではないことがわかるかもしれません

...
                       Asset       Size  Chunks                    Chunk Names
main.205199ab45963f6a62ec.js     544 kB       0  [emitted]  [big]  main
                  index.html  197 bytes          [emitted]
...

これは、webpackが特定ボイラープレート、特にランタイムとマニフェストをエントリチャンクに含めているためです。

ボイラープレートの抽出

コード分割で学んだように、SplitChunksPluginを使用すると、モジュールを別々のバンドルに分割できます。Webpackは、optimization.runtimeChunkオプションを使用して、ランタイムコードを別のチャンクに分割する最適化機能を提供します。これをsingleに設定すると、すべてのチャンクに対して単一のランタイムバンドルが作成されます。

webpack.config.js

  const path = require('path');
  const HtmlWebpackPlugin = require('html-webpack-plugin');

  module.exports = {
    entry: './src/index.js',
    plugins: [
      new HtmlWebpackPlugin({
      title: 'Caching',
      }),
    ],
    output: {
      filename: '[name].[contenthash].js',
      path: path.resolve(__dirname, 'dist'),
      clean: true,
    },
+   optimization: {
+     runtimeChunk: 'single',
+   },
  };

もう一度ビルドを実行して、抽出されたruntimeバンドルを確認してみましょう。

Hash: 82c9c385607b2150fab2
Version: webpack 4.12.0
Time: 3027ms
                          Asset       Size  Chunks             Chunk Names
runtime.cc17ae2a94ec771e9221.js   1.42 KiB       0  [emitted]  runtime
   main.e81de2cf758ada72f306.js   69.5 KiB       1  [emitted]  main
                     index.html  275 bytes          [emitted]
[1] (webpack)/buildin/module.js 497 bytes {1} [built]
[2] (webpack)/buildin/global.js 489 bytes {1} [built]
[3] ./src/index.js 309 bytes {1} [built]
    + 1 hidden module

また、lodashreactなどのサードパーティライブラリは、ローカルソースコードよりも変更される可能性が低いため、別のvendorチャンクに抽出するのが良いでしょう。このステップにより、クライアントは最新の状態を維持するために、サーバーからのリクエストをさらに減らすことができます。これは、SplitChunksPlugincacheGroupsオプションを使用することで実現できます。SplitChunksPluginの例2で実証されています。optimization.splitChunksに次のパラメーターを持つcacheGroupsを追加して、ビルドしてみましょう。

webpack.config.js

  const path = require('path');
  const HtmlWebpackPlugin = require('html-webpack-plugin');

  module.exports = {
    entry: './src/index.js',
    plugins: [
      new HtmlWebpackPlugin({
      title: 'Caching',
      }),
    ],
    output: {
      filename: '[name].[contenthash].js',
      path: path.resolve(__dirname, 'dist'),
      clean: true,
    },
    optimization: {
      runtimeChunk: 'single',
+     splitChunks: {
+       cacheGroups: {
+         vendor: {
+           test: /[\\/]node_modules[\\/]/,
+           name: 'vendors',
+           chunks: 'all',
+         },
+       },
+     },
    },
  };

もう一度ビルドを実行して、新しいvendorバンドルを確認してみましょう。

...
                          Asset       Size  Chunks             Chunk Names
runtime.cc17ae2a94ec771e9221.js   1.42 KiB       0  [emitted]  runtime
vendors.a42c3ca0d742766d7a28.js   69.4 KiB       1  [emitted]  vendors
   main.abf44fedb7d11d4312d7.js  240 bytes       2  [emitted]  main
                     index.html  353 bytes          [emitted]
...

これで、mainバンドルにnode_modulesディレクトリからのvendorコードが含まれておらず、サイズが240バイトに縮小されていることがわかります!

モジュール識別子

もう1つモジュールprint.jsをプロジェクトに追加してみましょう。

プロジェクト

webpack-demo
|- package.json
|- package-lock.json
|- webpack.config.js
|- /dist
|- /src
  |- index.js
+ |- print.js
|- /node_modules

print.js

+ export default function print(text) {
+   console.log(text);
+ };

src/index.js

  import _ from 'lodash';
+ import Print from './print';

  function component() {
    const element = document.createElement('div');

    // Lodash, now imported by this script
    element.innerHTML = _.join(['Hello', 'webpack'], ' ');
+   element.onclick = Print.bind(null, 'Hello webpack!');

    return element;
  }

  document.body.appendChild(component());

もう一度ビルドを実行すると、mainバンドルのハッシュのみが変更されると予想されますが...

...
                           Asset       Size  Chunks                    Chunk Names
  runtime.1400d5af64fc1b7b3a45.js    5.85 kB      0  [emitted]         runtime
  vendor.a7561fb0e9a071baadb9.js     541 kB       1  [emitted]  [big]  vendor
    main.b746e3eb72875af2caa9.js    1.22 kB       2  [emitted]         main
                      index.html  352 bytes          [emitted]
...

...3つすべてが変更されていることがわかります。これは、各module.idがデフォルトで解決順序に基づいてインクリメントされるためです。つまり、解決の順序が変更されると、IDも変更されます。要約すると

  • mainバンドルは、新しいコンテンツのために変更されました。
  • vendorバンドルは、そのmodule.idが変更されたために変更されました。
  • そして、runtimeバンドルは、新しいモジュールへの参照が含まれるようになったために変更されました。

最初と最後は予想どおりですが、修正したいのはvendorハッシュです。optimization.moduleIds'deterministic'オプションを使用しましょう。

webpack.config.js

  const path = require('path');
  const HtmlWebpackPlugin = require('html-webpack-plugin');

  module.exports = {
    entry: './src/index.js',
    plugins: [
      new HtmlWebpackPlugin({
        title: 'Caching',
      }),
    ],
    output: {
      filename: '[name].[contenthash].js',
      path: path.resolve(__dirname, 'dist'),
      clean: true,
    },
    optimization: {
+     moduleIds: 'deterministic',
      runtimeChunk: 'single',
      splitChunks: {
        cacheGroups: {
          vendor: {
            test: /[\\/]node_modules[\\/]/,
            name: 'vendors',
            chunks: 'all',
          },
        },
      },
    },
  };

これで、ローカルの依存関係が新しくなっても、vendorハッシュはビルド間で一貫性を保つはずです。

...
                          Asset       Size  Chunks             Chunk Names
   main.216e852f60c8829c2289.js  340 bytes       0  [emitted]  main
vendors.55e79e5927a639d21a1b.js   69.5 KiB       1  [emitted]  vendors
runtime.725a1a51ede5ae0cfde0.js   1.42 KiB       2  [emitted]  runtime
                     index.html  353 bytes          [emitted]
Entrypoint main = runtime.725a1a51ede5ae0cfde0.js vendors.55e79e5927a639d21a1b.js main.216e852f60c8829c2289.js
...

そして、src/index.jsを修正して、一時的にその追加の依存関係を削除しましょう。

src/index.js

  import _ from 'lodash';
- import Print from './print';
+ // import Print from './print';

  function component() {
    const element = document.createElement('div');

    // Lodash, now imported by this script
    element.innerHTML = _.join(['Hello', 'webpack'], ' ');
-   element.onclick = Print.bind(null, 'Hello webpack!');
+   // element.onclick = Print.bind(null, 'Hello webpack!');

    return element;
  }

  document.body.appendChild(component());

最後に、もう一度ビルドを実行しましょう。

...
                          Asset       Size  Chunks             Chunk Names
   main.ad717f2466ce655fff5c.js  274 bytes       0  [emitted]  main
vendors.55e79e5927a639d21a1b.js   69.5 KiB       1  [emitted]  vendors
runtime.725a1a51ede5ae0cfde0.js   1.42 KiB       2  [emitted]  runtime
                     index.html  353 bytes          [emitted]
Entrypoint main = runtime.725a1a51ede5ae0cfde0.js vendors.55e79e5927a639d21a1b.js main.ad717f2466ce655fff5c.js
...

どちらのビルドも、vendorバンドルのファイル名で55e79e5927a639d21a1bを生成したことがわかります。

結論

キャッシュは複雑になる可能性がありますが、アプリケーションまたはサイトのユーザーにとってのメリットは、労力を費やす価値があります。詳細については、以下の参考資料セクションを参照してください。

さらに読む

12 貢献者

okonetjouni-kantolaskipjackdannycjonesfadysamirsadekafontcurosavagesaiprasad2595EugeneHlushkoAnayaDesignaholznersnitin315