webpack@5の主な変更点まとめ
目次
予定では、明日の 10 日に webpack のメジャーバージョンである v5 がリリースされますが、まだエコシステムが安定していない可能性があるため、注意してアップグレードを行ってください。
webpack 5 release plan · Issue #11406 · webpack/webpack
TL;DR: release planned for 2020-10-10 After nearly 1 year of beta testing and about 2 years of development we feel that it's time to release webpack 5 soon. As promised we do announce the release a...
change log: https://github.com/webpack/changelog-v5
移行ガイド: https://webpack.js.org/migrate/5
追加機能
Persistent Caching
このバージョンからは今までメモリ上でしか行ってなかったファイルシステムによるキャッシュが導入されます。以下のように設定することにより、大幅な速度改善が見込めます。
module.exports = { cache: { type: "filesystem", buildDependencies: { config: [__filename], }, },};なし ↓
asset main.js 36.3 KiB [emitted] [minimized] (name: main)orphan modules 584 KiB [orphan] 554 modulescacheable modules 117 KiB ./src/index.js + 103 modules 117 KiB [built] [code generated] ./src/foo.js 21 bytes [built] [code generated]webpack 5.0.0-rc.2 compiled successfully in 1836 msあり ↓
asset main.js 36.3 KiB [compared for emit] [minimized] (name: main)cached modules 700 KiB [cached] 556 moduleswebpack 5.0.0-rc.2 compiled successfully in 429 ms詳しくは以下の記事を参照にしてください。
webpack@5で入るPersistent Cachingについて - hiroppy's site
新しく入ったPersistent cachingの機能について紹介します
Module Federation
リポジトリ間(バンドル間)を跨ぐときにライブラリなどの重複しているコードを以下のように効率よく扱いバンドルサイズを下げる仕組みです。この機能はお互いの webpack と連携を取り合う必要があるため互いに webpack@5 である必要があります。

詳しくは以下の記事を参考にしてください。
webpack@5で入るModule Federationについて - hiroppy's site
新しく入ったModule Federationの機能について紹介します
assetModules type の追加
今まで画像などを読み込むときに、file-loader や url-loader, raw-loader などを使っていましたがそれがネイティブサポートされました。
module.exports = { output: { assetModuleFilename: "images/[hash][ext]", }, module: { rules: [ { test: /\.(png|jpg|gif)$/, type: "asset/resource", }, ], },};詳しくは以下の記事を参考にしてください。
webpackの次のバージョンで入るassetModulesの紹介 - hiroppy's site
新しく入ったAsset Modulesの機能について紹介します
チャンク名が ID へ変更
今まで以下のようにwebpackChunkNameと書かなければ読めないファイル名となっていましたが、人が読める形となります。それに伴い、開発中でのwebpackChunkNameの指定をする必要がなくなることが期待されます。
(async () => { await import(/* webpackChunkName: "foo" */ "./foo");})();名前をつけたときの出力
asset main.js 2.79 KiB [emitted] [minimized] (name: main)asset foo.js 114 bytes [emitted] [minimized] (name: foo)runtime modules 7.23 KiB 10 modulescacheable modules 217 bytes ./src/index.js 190 bytes [built] [code generated] ./src/foo.js 27 bytes [built] [code generated]webpack 5.0.0-rc.4 compiled successfully in 274 msv5 のデフォルトでは以下のようにdeterministicという設定の新しいアルゴリズムが追加され、モジュール/チャンクの名前に 3~4 桁の数値 ID が付与されるようになります。これにより、ハッシュ化されたモジュール ID による gzip でのパフォーマンス低下は修正されました。
asset main.js 2.79 KiB [emitted] [minimized] (name: main)asset 717.js 114 bytes [emitted] [minimized]runtime modules 7.23 KiB 10 modulescacheable modules 186 bytes ./src/index.js 159 bytes [built] [code generated] ./src/foo.js 27 bytes [built] [code generated]webpack 5.0.0-rc.4 compiled successfully in 283 msファイル名を自動的に付与したい場合
module.exports = { optimization: { chunkIds: "named", },};123 bytes [emitted] [minimized] runtime modules 7.23 KiB 10 modules cacheablemodules 186 bytes ./src/index.js 159 bytes [built] [code generated] ./src/foo.js27 bytes [built] [code generated] webpack 5.0.0-rc.4 compiled successfully in256 ms ```
`optimization.chunkIds` に `named` を追加すればファイル名が確定しますが本番環境では表示されていいものなのかを検討してください。また、`optimization.splitChunks.name`はなくなったのでこちらに移行してください。
### import.meta のサポート
```javascript// ./src/index.jsconsole.log(import.meta.url);console.log(import.meta.webpack);生成されたファイルは固定値として入り、import.meta.url, webpackは存在しなくなるconsole.log("file:///Users/hiroppy/webpack/src/index.js"); console.log(5); ```
また、HMR 時に今までは以下のように書いていましたが、これからは`import.meta.webpackHot`を使うことが可能です。これを使うことにより、Node.js の module への依存を減らし、ESM に沿うような書き方に変わります。
```javascript// <= 4if (module.hot) { module.hot.accept();}
// >= 5if (import.meta.webpackHot) { import.meta.webpackHot.accept();}
// orimport.meta.webpackHot?.accept();data, file, http(s)のプロトコルのサポート
import x from "data:text/javascript,export default 42";console.log(x); // 42
import y from "file:///Users/hiroppy/webpack/src/index.js";また、フラグメント(#)もサポートされました。
const eIndexOf = require("es5-ext/array/\0#/e-index-of#fragment");http(s)プロトコルは、まだ完全にサポートされていないため以下の設定が必要です。
const webpack = require("webpack");
module.exports = { plugins: [ new webpack.experiments.schemes.HttpUriPlugin(), new webpack.experiments.schemes.HttpsUriPlugin(), ],};
// index.jsimport codeOfConduct from "https://raw.githubusercontent.com/webpack/webpack/master/CODE_OF_CONDUCT.md";console.log(codeOfConduct);Native Worker のサポート
new Worker(new URL('...', import.meta.url))が WebWorker を作るようにサポートされました。これは SharedWorker も同様です。
const fooWorker = new SharedWorker( new URL("./foo-worker.js", import.meta.url), { name: "foo", },);publicPath の自動化
新しくデフォルト値としてautoが追加され、document.currentScript, document.getElementsByTagName('script'), self.location の中から自動的に決定されます。注意点として、IE ではdocument.currentScriptがサポートされていないため、deferred か async のスクリプトには使用することができません。
module.exports = { output: { publicPath: "auto", },};Tree Shaking の最適化
ネストされたモジュールの場合、今までは使われていないbは削除できませんでしたが v5 からは追跡可能となりできるようになりました。
export const a = 1;export const b = 2;
// module.jsimport * as inner from "./inner";export { inner };
// user.jsimport * as module from "./module";console.log(module.inner.a);v4 では、モジュールの関係性しか見ていませんでしたが、v5 から入ったoptimization.innerGraphにより、内部モジュールへの最適化も行えるようになりました。
import { something } from "./something";
function usingSomething() { return something;}
export function test() { return usingSomething();}以下のケースが対象です。
- 関数宣言
- クラス宣言
- 変数宣言 及び
export default
Optimization.sideEffectsでは、ソースコードから副作用のないモジュールの単純なケースを検出できるようになりました。クラスおよび関数宣言、簡単な init 式を使用した変数宣言、if、while、for、switch、export、import、簡単なフラグを使用した関数呼び出し 等です。
また、CJS もサポートされました。
module.exports = require('...')module.exports.a.b.c = require('...').a.b.cObject.defineProperty(module.exports, 'xxx', ...)require('abc').xxx
このサポートは、ESM、CJS 間でも動くので、今後どちらのモジュールシステムを使っているかを気にせずに最適化行えるようになります。
これは別の記事で詳細に説明するので予定です。
output.filename, output. chunkFilename の関数化
output.filenameは今まで文字列しか受け取りませんでしたが、関数にすることが可能となったため更に柔軟な設定を表現することが可能となります。
module.exports = { output: { filename: ({ chunk }) => { if (chunk.name === "main") return "main.bundle.[contenthash].js"; return "foo.bundle.[contenthash].js"; }, },};externalsType の追加
externalsTypeにpromise, import, scriptが追加され、より柔軟に対応できるようになりました。
- promise:
varと同様だが、非同期モジュールとなる - import:
import()を使い、非同期のネイティブ ESM モジュールを読み込む - script:
<script>を使い、事前に定義されたグローバル変数を公開するスクリプトを読み込む
module.exports = { externalsType: "promise",};target の詳細化と browserslist のサポート
targetに対して、詳細な設定ができるようになりました。 配列を受け取るようになり、target: ['web', 'es2015'] 等の書き方が行えるようになりました。 また、browserslist がされたため、webの場合はtargetの設定は不要となります。
GitHub - browserslist/browserslist: 🦔 Share target browsers between different front-end tools, like Autoprefixer, Stylelint and babel-preset-env
🦔 Share target browsers between different front-end tools, like Autoprefixer, Stylelint and babel-preset-env - browserslist/browserslist
デフォルト値はtarget: 'browserslist'となり、フォールバック先は変わらずにwebとなります。
TypeScript 型定義ファイルの提供
@types/webpackは不要になりました。
import { WebpackOptionsNormalized } from "webpack";
const config: WebpackOptionsNormalized = { entry: "index.js", output: { filename: "bundle.js", },};splitChunks でのサイズ設定値の変更
今までは、JS のみのチャンクサイズでしたが、さらに詳細に指定できるようになりました。
module.exports = { optimization: { splitChunks: { cacheGroups: { test: { name: "test", minSize: { javascript: 100, webassembly: 100, style: 100, }, }, }, }, },};また、本番環境でのminSizeのデフォルト値は20kとなりました。
実験的段階
top-level-await のサポート
シンタックスは ESM の仕様に沿いますが、まだ stage-3 なので実験的フェーズです。
GitHub - tc39/proposal-top-level-await: top-level `await` proposal for ECMAScript (stage 4)
top-level `await` proposal for ECMAScript (stage 4) - tc39/proposal-top-level-await
module.exports = { experiments: { topLevelAwait: true, },};import("file:///Users/hiroppy/Desktop/webpack-5/src/foo.js");
console.log(x);script タグでのモジュールサポート
バンドル時に使われる IIFE が取り除かれ、<script type="module">経由で呼び出される形に出力されます。この場合、仕様に沿い厳格モードと遅延ロードが有効化されます。
module.exports = { experiments: { outputModule: true, },};破壊的変更
最低要求バージョンが Node.js@10 へ
webpack 及び webpack のコアにおけるエコシステムが要求する Node.js のバージョンの最低値は 10 となります。
Node.js の polyfill の自動挿入が廃止
メンバー間でも賛否両論がありましたが、理由としては以下のような目的があります。
- webpack は web へ向かっている
- polyfill 自体が完全互換なものではない
- メンテナンスコストの高さ
自分が経験した例としては、processやutilに依存している Node.js のコードをクライアントサイドで使う場合があり、v5 に上げたら動かなくなる場合があります。
実際に webpack4 まで使っていた polyfill は以下のリポジトリで管理されているので、これを参考にして各自で追加する必要があります。
GitHub - webpack/node-libs-browser: [DEPRECATED] The node core libs for in browser usage.
[DEPRECATED] The node core libs for in browser usage. - webpack/node-libs-browser
これに伴い、node.*の中のネイティブモジュールがすべて廃止となります。 また、global, __filename, __dirnameはデフォルトでfalseの値となります。
module.exports = { node: { // Buffer: false, これは廃止 global: false, __filename: false, __dirname: false, },};JSON での named export の禁止
ESM の仕様上、これは許可されていないためこれが行われているコードの場合警告が出るようになるため、以下のように変更する必要があります。
// 😵import { version } from "./package.json";
// 🙂import package from "./package.json";const { version } = package;loader と use の違いを厳格化
rules.loaderとrules.useで目的に合ってない使い方の設定の場合、エラーを吐くようになりました。 useはoptionsがない場合のみ使用可能(引数は受け入れ可)となり、optionsがある場合はloaderを使わなければなりません。
デフォルトランタイムが一部 ES2015 へ変更
webpack の生成するコードのデフォルトが一部 es5 から es2015 となります。
これはあくまでもバンドルサイズを減らすことが目的なため、varからconstにはなったりせず、functionを() => {} となります。
もし IE をサポートしている場合は以下を追加する必要があります。
module.exports = { target: ["web", "es5"],};また、これは追加機能として用意された browserslist を用いて回避することも可能です。
# browserslistlast 1 version