dackdive's blog

新米webエンジニアによる技術ブログ。JavaScript(React), Salesforce, Python など

webpack 3 リリース内容まとめ

2系に上げるかどうかを悩んでいる間に 3.0.0 がリリースされてしまった。

を参考にアップデート内容をメモ。

概要、v2 からのマイグレーション

大きな feature は Scope Hoisting と Magic Comment ぐらい。
v2 からの移行も特別な作業は必要なく、たいていはバージョン上げるだけで機能するとのこと。

それでもメジャーバージョンを上げたのは内部的に breaking changes があって、いくつかのプラグインには影響しそうだったから、とのこと。
ということはプラグインが動くかどうか一応確認した方がいい...?


Scope Hoisting

目玉機能その1。
これまでの webpack ではモジュールを1つ1つクロージャでラップしており、それによりそれぞれのモジュールの独立性が保たれていた(関数名の衝突とか)ものの、ブラウザでの実行速度が重くなる原因になっていた。
これに対し Closure Compiler やRollupJS は各モジュールを連結して1つのスコープにまとめてしまうので(hoist)ブラウザでの実行速度が速い。
(これができるのは ES Modules の import/export によって静的にモジュールの依存関係がわかるから?みたいなことが書かれていた)

webpack3 では以下のプラグインを指定することにより、RollupJS などと同じ Scope Hoisting が可能になった。

module.exports = {  
  plugins: [
    new webpack.optimize.ModuleConcatenationPlugin()
  ]
};

余計な関数でのラップがなくなったのでバンドル後のファイルサイズも小さくなったが、注目すべきは上述したように実行速度の向上ぽい。


参考

webpack freelancing log book (week 5–7) | by Tobias Koppers | webpack | Medium

冒頭のブログからリンクされていた。
Scope Hoisting のメリットや webpack でこれを実現するためにどうしたかみたいなことが詳しく書かれている。
特に気になったのは Scope Hoisting によるデメリットのところで、

There are some disadvantages, but most of them are not really relevant, when Scope Hoisting is seen as production-only optimization.

  • HMR can only replace modules if they are isolated with a function wrapper. (DX)
  • Minimizing can be more expensive when the scope is bigger. (Build Performance)
  • Modules can no longer processed individually, but need to be processed combined. (Build Performance)

HMR が効かなくなるみたいだし、プラグインは Production Build 時のみ有効にした方が良さそう。

サンプル

たとえば、

// main.js
import cube from './module_1';
import cube2 from './module_2';

console.log(cube(5) + cube2(5));

// module_1.js
export default function cube(x) {
    return x * x * x;
}

// module2.js
export default function cube(x) {
    return x * x * x;
}

というモジュール(module1 と 2 で同一名の関数をエクスポート)があって、
webpack 2.6.1 だと

/******/ (function(modules) { // webpackBootstrap

...

/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = cube;
function cube(x) {
  return x * x * x;
}


/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = cube;
function cube(x) {
  return x * x * x;
}

...

というようにたしかに関数でラップされているが、webpack 3.0.0 だと

/******/ (function(modules) { // webpackBootstrap

...

/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });

// CONCATENATED MODULE: ./module-1.js
function cube(x) {
  return x * x * x;
}

// CONCATENATED MODULE: ./module-2.js
function module_2_cube(x) {
  return x * x * x;
}

...

というように関数のラップがなくなり、関数名の衝突も自動的に解決してくれてる。
※それ以外の部分の理解ができていないので解釈が間違ってるかもしれないけど

Rollup.js だと REPL で確認した限りもっと簡潔になる


Magic Comments

目玉機能その2。
元々、v2 から import() による動的インポートが可能になっていた。
(参考:webpack2でTree Shaking以外に何ができるようになったのかメモ - dackdive's blog

動的にインポートする部分のモジュールは chunk として別のファイルに出力されていたが、その名前が付けられないせいで 0.bundle.js のようなわかりづらいファイルが生成されていた。

webpack3 では import() 時に以下のコメントを入れることで任意のチャンクファイル名をつけることができる。

import(/* webpackChunkName: "my-chunk-name" */ 'module');

未検証。
※v2.4 あたりから既にできていたらしい


参考

より詳しくは公式ドキュメントのここを読んでくれとのこと。
https://webpack.js.org/guides/code-splitting-async/


その他の機能

https://github.com/webpack/webpack/releases/tag/v3.0.0 を見るしかないのかな?

  • output.libraryExport をサポート。default しかエクスポートしないよう選択可能に(ライブラリ向け)
  • node: false とすると Node.js 向けの機能すべて無効化

あたりが気になったが、詳細は調べきれてません。


今後

どこかのタイミングでリリースに含める feature は投票(vote)制になったらしい。
https://webpack.js.org/vote/