53
49

More than 5 years have passed since last update.

WebpackでDynamic importを行ってみる

Last updated at Posted at 2018-03-07

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()実行まで中身は実行されません。

外部リンク

53
49
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
53
49