dackdive's blog

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

webpack2でTree Shaking以外に何ができるようになったのかメモ

メモ。
webpack 2 正式リリース(バージョンは 2.2)だとか Tree Shaking という機能がいいらしい とかは目にしていたけど
結局 v1 -> v2 とメジャーバージョンが上がって Tree Shaking 以外に何が変わったの?というのがよくわからなかったので調べてみた。
なお、Tree Shaking については最後に記載している。

参考にしたサイト

「webpack 2 whats new」とかでググって、見つけられたのはここ。

What's new in webpack 2 · GitHub

(WIP て書いていて最新の情報かどうかわからないけど)ここに書いてある内容で気になったものをピックアップする。

なお、Getting Started with webpack 2 という記事は残念ながら v1 → v2 における新機能などについては言及されていなかった。


v2 からできるようになったこと

ES6 Modules をサポート

Babel を使ってトランスパイルしなくても import, export をそのまま解釈できるようになった。
ただし、Babel なしだと import/export 以外の ES2015 をトランスパイルしてくれるわけではない ので注意。
(こちらで確認した限り、たとえば constconst のまま、という意味)

また、Babel なしだと webpack.config.babel.js というファイル名で ES2015 形式で書けるようにはならないので注意。


System.Import による動的インポート


(2017/02/08追記)

Twitter

というのを教えていただいた。

確認したところ
Release v2.1.0-beta.28 · webpack/webpack

でたしかに

add import() as Code Splitting construct. It should be used instead of System.import when possible. System.import will be deprecated in webpack 2 release (removed in webpack 3) as it's behavior is incorrect according to the spec.

という記載があったので、System.import() のかわりに単に import() を使った方がいい。
試していないが、以下 System.import() 部分を import() に置き換えても動くはず。

(追記ここまで)


コード中に System.import('モジュールへのパス') と書くことで動的にモジュールをインポートできる。
reuqire と役目は同じ。

function onClick() {
    System.import("./module").then(module => {
        module.default;
    }).catch(err => {
        console.log("Chunk loading failed");
    });
}

特徴としては System.Import の結果が Promise として返される ところ。
これによりモジュール読み込みに失敗したときにこちら側でハンドリングすることができる。

System.Import で読み込もうとしているモジュールはチャンクファイルとして本体のファイルとは別のファイルが生成されるよう。

たとえば上のコードを index.js として、ディレクトリ構成が

├── src
│   ├── index.js
│   └── module.js
└── webpack.config.js

webpack.config.js

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'build')
  },
  ...

となっていた場合、ビルド後は

├── build
│   ├── 0.bundle.js
│   └── bundle.js

となった。

また、

System.import("./routes/" + path + "/route")

のように変数で指定することも可能。
この場合は(require のときも同じだと思うが)webpack は可能性のあるファイルを自動的に判別してくれる。
このときも可能性のあるモジュールファイルごとにチャンクファイルが生成される。


webpack.config.js で関数を export することが可能に

v1 のときは、webpack.config.js は

module.exports = {
  entry: './src/index.js',
  ...
}

というようにオブジェクトを export していたが、関数を export することもできるようになった。

module.exports = function(options) {
  return {
    entry: './src/index.js',
    ...
  };
}

これで何が嬉しいのかと言うと、コマンドライン実行時に --env を指定することでこの関数に任意の設定情報を渡せるようになった

--env foo

のように文字列を渡すか、

--env.foo 1 --env.bar 2

のようにするとオブジェクトを渡すことができる。

例:
// webpack.config.js
module.exports = function(options) {
  console.log(options);
  return {
    entry: './src/index.js',
    ...
# "scripts": { "webpack": "webpack" } は package.json に記載済み
$ npm run webpack -- --env foo
> webpack2-playground@1.0.0 webpack /Users/yamazaki/workspace/webpack2-playground
> webpack "--env" "foo"

foo

$ npm run webpack -- --env.foo 1 --env.bar 2

> webpack2-playground@1.0.0 webpack /Users/yamazaki/workspace/webpack2-playground
> webpack "--env.foo" "1" "--env.bar" "2"

{ foo: 1, bar: 2 }


webpack.config.js の書き方が一部変更

これは機能ではないが一応。
Migrating from v1 to v2 に従ってマイグレーションが必要。


おまけ:Tree Shaking について

最後におそらく webpack2 の目玉機能である Tree Shaking について。
すごいざっくりとした理解だけど、「export してるけどどこからも import されていないモジュールをバンドル時に自動的に削除して、ファイルサイズを小さくしてくれる」機能のよう。

Tree Shaking を利用するには Babel で import/export をトランスパイルしないように設定してあげる必要があり、具体的には .babelrc

{
  "presets": ["es2015"]
}

としていたところを

{
  "presets": [["es2015", { "modules": false }]]
}

のように変更してあげる必要がある。

ただ、

という問題にぶち当たった。これは、es2015 の preset を webpack.config.js に寄せるしかないのか...

日本語の解説記事だと冒頭でもリンク貼ったけどこちらが参考になる。

また、英語だと

Webpack 2 Tree Shaking Configuration – Modus Create: Front End Development – Medium
はわかりやすいが若干情報が古く、ただ サンプルコード の方は情報がアップデートされてるので参考にできる。

あとは
Tree-shaking ES6 Modules in webpack 2
だろうか。


おわりに

参考にしたサイト自体の情報が古かったり間違ってたりする可能性はあるが、とりあえず↑に書いたことは自分でも動作確認できた(webpack のバージョンは 2.2.1)。
日頃からリポジトリをウォッチしてたり changelog 見てたら正しい情報をキャッチアップし続けられるんだろうけど、そういうことできる人はほんとすごいなと思う。
(私はこういうことが知りたいと思ったときにどこを見るようにすればいいのかもよくわかってません...)