したがって、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
また、lodash
やreact
などのサードパーティライブラリは、ローカルソースコードよりも変更される可能性が低いため、別のvendor
チャンクに抽出するのが良いでしょう。このステップにより、クライアントは最新の状態を維持するために、サーバーからのリクエストをさらに減らすことができます。これは、SplitChunksPlugin
のcacheGroups
オプションを使用することで実現できます。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
を生成したことがわかります。
キャッシュは複雑になる可能性がありますが、アプリケーションまたはサイトのユーザーにとってのメリットは、労力を費やす価値があります。詳細については、以下の参考資料セクションを参照してください。