コード分割
このサイトは作成途中のものを公開しています。 また、Webpackのバージョンが1.13.0の頃に作成されたものですが、2017年2月現在Webpackは1系を非推奨として2の使用を勧めています。
大規模なアプリケーションにおいて、全てのコードを1つのファイルに含めてしまうのは得策とは言えません。 特に、特定のコードのブロックがある階層下でしか必要とされないケースでは、それが顕著に言えるでしょう。 webpackは必要に応じて、読み込む"chunks"(チャンク/固まり)毎にコードを分割する機能を備えています。 別のバンドラーでは、これを"layers"、"rollups"、"fragments"などと呼んでいます。 この機能は、"code splitting"(コード分割)と呼ばれています。
これは、任意で選択する機能になります。 開発者がコードに分割ポイント(split points)を定義することになります。 webpackは出力されるファイルと実行時の依存性を考慮して処理してくれます。
よくある誤解が起きないように、コード分割とは単に共通するコードを共有されるチャンクに抜き出すだけでは無い、ということを明らかにしておきます。 それよりも、コード分割は読み込まれるチャンクに応じてコードを分割できるということが重要です。 これは初回のダウンロードサイズを小さくし、アプリケーションによるリクエストに応じてコードをダウンロードさせることを可能にします。
- 分割ポイントの定義
- チャンクの内容
- チャンクの最適化
- チャンクの読み込み
- チャンクのタイプ
- 複数のエントリー・チャンク
- 共通(commons)チャンク
- 最適化
- チャンクの名付け
- require.include
分割ポイントの定義
AMDとCommonJsで、必要なコードを読み込むメソッド(方法)が異なりますが、 どちらにも分割ポイントがサポートされています。
CommonJsの場合: require.ensure
require.ensure(dependencies, callback)
require.ensure
メソッドは、dependencies
の全ての依存性がcallback
が呼びだされた際に同期的にrequireされることを保証します。
callback
は、パラメーターとしてrequire
関数が指定された状態で呼び出されます。
//例:
require.ensure(["module-a", "module-b"], function(require) {
var a = require("module-a");
// ...
});
注意:
require.ensure
は、あくまでモジュールを読み込むだけで、それらを評価するわけではありません。
AMDの場合: require
AMDの仕様で、これを記述するための非同期のrequire
メソッドが定義されています。
require(dependencies, callback)
これが呼び出されると、全てのdependencies
指定したモジュールが読み込まれ、
callback
は読み込まれたdependencies
のexport付き(引数として)で呼びだされます。
//例:
require(["module-a", "module-b"], function(a, b) {
// ...
});
注意:
AMDの場合、require
はモジュールの読み込みと評価を行います。
webpackではモジュールは左から右へ評価されます。
注意:
コールバック(callback
)は省略することができます。
ES6モジュール
要約:
webpackはes6モジュールをサポートしないため、
require.ensure
またはrequire
を、
使用するトランスパイラが作成するモジュールフォーマットの依存性に直接使用してください。
webpack 1.x.x(2.0.0ではサポート)は、ES6モジュールをサポートしません。
ただし、Babelのようなトランスパイラを使用して、ES6のimport
文をCommonJSまたはAMDモジュールに変換することで、これを可能にすることも出来ます。
このアプローチは有効ではあるものの、動的読み込みにおいては1つ重大な注意点があります。
追加されたモジュール文法(import x from 'foo'
)は、意図的に静的解析されるように設計されているため、
動的なimportは不可能ということになります。
// 無効!!!!!!!!!
['lodash', 'backbone'].forEach(name => import name )
幸い、JavaScriptのAPIの"loader"の仕様には、動的なユースケースに対する扱いが書かれています。(System.load
、またはSystem.import
)
このAPIは、ネイティブにおける上記のrequire
に相当するものになるでしょう。
しかしながら、ほとんどのトランスパイラはSystem.load
呼び出しのrequire.ensure
への変換をサポートしないため、
もし動的なコード分割をさせたければ、開発者が直接これを行う必要があります。
// 静的なimport
import _ from 'lodash'
// 動的なimport
require.ensure([], function(require) {
let contacts = require('./contacts')
})
チャンクの内容
分割ポイントの全ての依存性は、新しいチャンクに入れられます。 また、依存関係は再帰的に追加されます。
もし、分割ポイントへコールバックとして関数式(または関数式へ紐付けされたもの)を渡す場合、 webpackはその関数式でrequireされる全ての依存性も、自動的にチャンクへ追加します。
チャンクの最適化
2つのチャンクが同じモジュールを含む場合、それらは1つにマージされます。 これにより、チャンクが複数の親を持つ可能性があります。
もし、あるモジュールがあるチャンクの全ての親で利用される場合、それはそのチャンクから削除されます。
もし、あるチャンクが別のチャンクの全てのモジュールを含む場合、それは保存され、複数のチャンクで必要とされます。
チャンクの読み込み
設定でのオプションのtarget
に応じて、チャンク読み込みのための実行ロジックが、バンドル(出力ファイル)へ追加されます。
例えば、targetがweb
であれば、チャンクはjsonpを介して読み込まれます。
チャンクは一度だけ読み込まれ、並列なリクエストは1つにマージされます。
読み込まれたチャンクに対して、それらが複数のチャンクで必要とされているか否かが実行時にチェックされます。
チャンクのタイプ
アプリケーション(app)とベンダー(vendor)のコードを分割
アプリケーションをapp.js
とvendor.js
の2つのファイルに分けようとした場合、
ベンダーのファイルをvendor.js
の中でrequireすることが考えられます。
その場合、そのベンダー名を下記のようにCommonsChunkPlugin
に渡します。
var webpack = require("webpack");
module.exports = {
entry: {
app: "./app.js",
vendor: ["jquery", "underscore", ...],
},
output: {
filename: "bundle.js"
},
plugins: [
new webpack.optimize.CommonsChunkPlugin(/* chunkName= */"vendor", /* filename= */"vendor.bundle.js")
]
};
これはapp
チャンクから、vendor
チャンク内の全てのモジュールを削除します。
この場合、bundle.js
にはappのコードだけが含まれ、その依存性は含まれず、
代わりにvendor.bundle.js
に含まれることになります。
HTMLページでは、bundle.js
の前に、vendor.bundle.js
を読み込みます。
<script src="vendor.bundle.js"></script>
<script src="bundle.js"></script>
複数のエントリー・チャンク
複数のエントリー・ポイントを設定することが可能で、 この場合、複数のエントリー・チャンクを持つことになります。 エントリー・チャンクはランタイムを含み、ページには1つだけランタイムが無ければいけまん。(例外あり)
複数のエントリー・ポイントの実行
CommonsChunkPlugin
に指定されたランタイムは、共通(commons)チャンクに移されます。
その場合、このエントリー・ポイントは、イニシャル・チャンク内になります。
イニシャル・チャンクは1つだけ読み込むことが出来る一方で、エントリー・チャンクは複数読み込むことが出来ます。
これは、単一のページで複数のエントリー・ポイントを実行出来ることを意味します。
// 例:
var webpack = require("webpack");
module.exports = {
entry: { a: "./a", b: "./b" },
output: { filename: "[name].js" },
plugins: [ new webpack.optimize.CommonsChunkPlugin("init.js") ]
}
<script src="init.js"></script>
<script src="a.js"></script>
<script src="b.js"></script>
共通(commons)チャンク
CommonsChunkPlugin
はモジュールを移すため、これにより複数のエントリー・チャンクが新しいチャンクになります。(共通(commons)チャンク)
同様に、ランタイムも共通チャンクに移されます。
これは、元々のエントリー・チャンクだったものが、イニシャル・チャンクになることを意味します。
詳細は、プラグイン一覧を参照してください。
最適化
特定の基準をもとにチャンクをマージすることが出来る、最適化プラグインが存在します。 プラグイン一覧を参照して下さい。
LimitChunkCountPlugin
MinChunkSizePlugin
AggressiveMergingPlugin
チャンクの名付け
require.ensure
関数は、特別な3つ目のパラメーターが用意されています。
これは文字列でなければいけません。
もし2つの分割ポイントが同じ文字列を渡した場合、それらは同じチャンクを使用します。
require.include
require.include(request)
require.include
はwebpackの特別な関数で、現在のチャンクにモジュールを追加しますが、それは評価されません。
(バンドルから、その文を削除します。)
// 例:
require.ensure(["./file"], function(require) {
require("./file2");
});
// 上記は下記と等価です。
require.ensure([], function(require) {
require.include("./file");
require("./file2");
});
require.include
は、モジュールが複数の子チャンク内にある場合に便利かもしれません。
親のrequire.include
がそのモジュールをincludeすることで、
子チャンク内で同じことをする手間が省かれます。
© 2010 - 2017 STUDIO KINGDOM