JavaScript
Import
es6
webpack

WebpackでDynamic importを行ってみる

More than 1 year has passed since last update.

ES6 Moduleには、import ~;と書く静的なものだけでなく、import()と関数っぽく書く、動的なものも登場しようとしています。すでに一部のブラウザに実装されていますが、Webpackでも利用可能です。


事前準備

Webpack本体はバージョン3でもすでにimport()を使えるようになっていますが、周辺ツールは対応するために事前準備が必要となります。



  • babel - import()をシンタックスエラーとせずスルーさせるために、babel-plugin-syntax-dynamic-importが必要です。


  • eslint - 標準ではimport()を文法エラーとしてしまうので、"parser": "babel-eslint"とする必要があります(もちろん、babel-eslintのインストールも必要です)。


コードを書いてみる

あとは特に設定は必要なく、import()で分割するファイルのエントリポイントを読み込めば、ファイル分割まで自動で行われます(もちろん、他の箇所で静的にimportされているものを動的にimport()しても、すでにバンドルされているものが使われるので、改めてファイルが発生することはありません)。

import()Promiseを返しますので(IE11などではPolyfillが必要です)、単純に.thenをつなげるなり、Promise.allで全部揃うまで待つなり、async-awaitで待たせるなり、ロード後のタイミングでの実行も問題ありません。

なお、Webpackが読み込むものを決められる必要があるので、import()の引数は完全に変数ではなく、文字列リテラルを含む式など、パスの手がかりが得られるものでないといけません。


分割後のファイル名を指定する

デフォルトでは、「0.js」のようなファイル名になってしまって、何が何だか分からないし、キャッシュパージも効かないので扱いづらいです。

これを改善するには、まずimport()の側にコメントを追加します。

import(/* webpackChunkName: "qiita" */'path/to/qiita').then(...);

こうすることでチャンクに名前が入るので、あとはwebpack.config.jsoutput.chunkFilenameリファレンス)で名前のパターンを指定できます。


挙動を確認する

まず、全体のエントリポイントの側に、webpackJsonpというグローバル関数が用意されます(名前は設定で変更可能です)。そして、import()の実行は以下のように処理されます。


  1. まず、ロード済みなら適切なライブラリをrequireして成功したPromiseを返す

  2. すでにロードが進行中なら、生成済みのPromiseを返す

  3. タイムアウトしたら失敗、ロードするファイルのwebpackJsonpが実行されれば成功、というようなPromiseを作成する


  4. <script>タグを<head>に追加


  5. Promiseを返す

ということで、


  • 複数回import()しても、重複してロードが走ることはありません。

  • 分割後のファイルを事前に<script>で読み込んであったような場合、読み込みは済むけど、import()実行まで中身は実行されません。


外部リンク