予定では、明日の10日にwebpackのメジャーバージョンであるv5がリリースされますが、まだエコシステムが安定していない可能性があるため、注意してアップグレードを行ってください。
change log:
移行ガイド:
- 追加機能
- Persistent Caching
- Module Federation
- assetModules typeの追加
- チャンク名がIDへ変更
- import.metaのサポート
- data, file, http(s)のプロトコルのサポート
- Native Workerのサポート
- publicPathの自動化
- Tree Shakingの最適化
- output.filename, output. chunkFilenameの関数化
- externalsTypeの追加
- targetの詳細化とbrowserslistのサポート
- TypeScript型定義ファイルの提供
- splitChunksでのサイズ設定値の変更
- 実験的段階
- 破壊的変更
追加機能
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 modules cacheable 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 modules webpack 5.0.0-rc.2 compiled successfully in 429 ms
詳しくは以下の記事を参照にしてください。
Module Federation
リポジトリ間(バンドル間)を跨ぐときにライブラリなどの重複しているコードを以下のように効率よく扱いバンドルサイズを下げる仕組みです。この機能はお互いのwebpackと連携を取り合う必要があるため互いにwebpack@5である必要があります。
詳しくは以下の記事を参考にしてください。
assetModules typeの追加
今まで画像などを読み込むときに、file-loaderやurl-loader, raw-loaderなどを使っていましたがそれがネイティブサポートされました。
module.exports = { output: { assetModuleFilename: 'images/[hash][ext]', }, module: { rules: [ { test: /\.(png|jpg|gif)$/, type: 'asset/resource' } ] } };
詳しくは以下の記事を参考にしてください。
チャンク名が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 modules cacheable 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 ms
v5のデフォルトでは以下のように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 modules cacheable 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' } };
asset main.js 2.8 KiB [emitted] [minimized] (name: main) asset src_foo_js.js 123 bytes [emitted] [minimized] runtime modules 7.23 KiB 10 modules cacheable 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 256 ms
optimization.chunkIds
に named
を追加すればファイル名が確定しますが本番環境では表示されていいものなのかを検討してください。また、optimization.splitChunks.name
はなくなったのでこちらに移行してください。
import.metaのサポート
// ./src/index.js console.log(import.meta.url); console.log(import.meta.webpack);
// ./dist/main.js // 生成されたファイルは固定値として入り、import.meta.url, webpackは存在しなくなる console.log("file:///Users/hiroppy/webpack/src/index.js"); console.log(5);
また、HMR時に今までは以下のように書いていましたが、これからはimport.meta.webpackHot
を使うことが可能です。これを使うことにより、Node.jsのmoduleへの依存を減らし、ESMに沿うような書き方に変わります。
// <= 4 if (module.hot) { module.hot.accept(); } // >= 5 if(import.meta.webpackHot) { import.meta.webpackHot.accept(); } // or import.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.js import 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からは追跡可能となりできるようになりました。
// inner.js export const a = 1; export const b = 2; // module.js import * as inner from "./inner"; export { inner } // user.js import * 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.c
Object.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
の設定は不要となります。
デフォルト値は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なので実験的フェーズです。
// webpack.config.js module.exports = { experiments: { topLevelAwait: true, } };
const x = await 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は以下のリポジトリで管理されているので、これを参考にして各自で追加する必要があります。
これに伴い、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
を使わなければなりません。
細かい変更ですが、ユーザーへの影響が大きいものの一つとして、webpack@5では以下のようなルールとなり、設定ファイルのエラーとなり、実行できなくなります。
— 蝉丸ファン (@about_hiroppy) 2020年9月14日
use -> optionsがない場合のみ使用可能(引数は受け入れ可)
loader -> loaderに引数を付ける場合、useに変更するように pic.twitter.com/qqmsoWWzWn
デフォルトランタイムが一部ES2015へ変更
webpackの生成するコードのデフォルトが一部es5からes2015となります。
これはあくまでもバンドルサイズを減らすことが目的なため、var
からconst
にはなったりせず、function
を() => {}
となります。
もしIEをサポートしている場合は以下を追加する必要があります。
module.exports = { target: ['web', 'es5'] };
また、これは追加機能として用意されたbrowserslistを用いて回避することも可能です。
# browserslist last 1 version