コード分割
Parcelは、設定なしでコード分割をサポートしています。これにより、アプリケーションのコードを別々のバンドルに分割し、必要に応じてロードできるため、初期バンドルのサイズが小さくなり、ロード時間が短縮されます。
コード分割は、通常のimport
ステートメントのように動作しますが、Promiseを返す動的なimport()
構文の使用によって制御されます。これは、モジュールを非同期にロードできることを意味します。
動的インポートの使用
#次の例は、動的インポートを使用して、アプリケーションのサブページを必要に応じてロードする方法を示しています。
import("./pages/about").then(function (page) {
// Render page
page.render();
});
export function render() {
// Render the page
}
import()
はPromiseを返すため、async/await構文も使用できます。
async function load() {
const page = await import("./pages/about");
// Render page
page.render();
}
load();
export function render() {
// Render the page
}
ツリーシェイキング
#Parcelが動的にインポートされたモジュールのどのエクスポートを使用するかを判別できる場合、そのモジュールから未使用のエクスポートをツリーシェイキングします。これは、await
またはPromiseの.then
構文を使用して、静的プロパティアクセスまたは分割代入で機能します。
注: await
の場合、未使用のエクスポートは、await
がトランスパイルされない場合(つまり、最新のbrowserslist設定の場合)にのみ削除できます。
let { x: y } = await import("./b.js");
let ns = await import("./b.js");
console.log(ns.x);
import("./b.js").then((ns) => console.log(ns.x));
import("./b.js").then(({ x: y }) => console.log(y));
共有バンドル
#アプリケーションの複数の部分が同じ共通モジュールに依存する場合、それらは自動的に個別のバンドルに重複排除されます。これにより、一般的に使用される依存関係をアプリケーションコードと並行してロードし、ブラウザーによって個別にキャッシュできます。
たとえば、アプリケーションに同じ共通モジュールに依存する<script>
タグを持つ複数のページがある場合、これらのモジュールは「共有バンドル」に分割されます。このようにして、ユーザーがあるページから別のページに移動する場合、そのページの新しいコードのみをダウンロードする必要があり、ページ間の共通の依存関係はダウンロードする必要がありません。
<!doctype html>
<div id="app"></div>
<script type="module" src="home.js"></script>
import { createRoot } from 'react-dom';
createRoot(app).render(<h1>Home</h1>, app);
<!doctype html>
<div id="app"></div>
<script type="module" src="profile.js"></script>
import { createRoot } from 'react-dom';
createRoot(app).render(<h1>Profile</h1>, app);
コンパイル済みHTML
<!doctype html>
<div id="app"></div>
<script type="module" src="react-dom.23f6d9.js"></script>
<script type="module" src="home.fac9ed.js"></script>
<!doctype html>
<div id="app"></div>
<script type="module" src="react-dom.23f6d9.js"></script>
<script type="module" src="profile.9fc67e.js"></script>
上記の例では、home.js
とprofile.js
の両方がreact-dom
に依存しているため、個別のバンドルに分割され、両方のHTMLページに追加の<script>
タグを追加することにより並行してロードされます。
これは、動的なimport()
でコード分割されたアプリの異なるセクション間でも機能します。2つの動的インポート間で共有される共通の依存関係は分割され、動的にインポートされたモジュールと並行してロードされます。
設定
#デフォルトでは、Parcelは共有モジュールがサイズしきい値に達した場合にのみ共有バンドルを作成します。これにより、非常に小さなモジュールを分割したり、HTTP/2でもオーバーヘッドのある追加のHTTPリクエストを作成したりすることを回避できます。モジュールがしきい値を下回っている場合は、ページ間で重複します。
Parcelには、一度に多数のリクエストでブラウザーに過負荷をかけることを回避するための最大並列リクエスト制限もあり、この制限に達するとモジュールが重複します。共有バンドルを作成する場合、より大きなモジュールがより小さなモジュールよりも優先されます。
デフォルトでは、これらのパラメーターはHTTP/2用に調整されています。ただし、これらのオプションを調整して、アプリケーションに対して上下させることができます。これは、プロジェクトルートのpackage.jsonで@parcel/bundler-default
キーを構成することで実行できます。
{
"@parcel/bundler-default": {
"minBundles": 1,
"minBundleSize": 3000,
"maxParallelRequests": 20
}
}
使用可能なオプションは次のとおりです。
- minBundles – アセットを分割するには、
minBundles
個を超えるバンドルで使用する必要があります。 - minBundleSize – 共有バンドルを作成するには、少なくとも
minBundleSize
バイトの大きさが必要です(圧縮とツリーシェイキングの前)。 - maxParallelRequests – 多すぎる同時リクエストでネットワークに過負荷をかけることを防ぐために、これにより、最大
maxParallelRequests
個の兄弟バンドルを一緒にロードできます。 - http – 上記の値をHTTP/1またはHTTP/2用に最適化されたデフォルト値に設定するための短縮形。これらのデフォルト値については、以下の表を参照してください。
http | minBundles | minBundleSize | maxParallelRequests |
---|---|---|---|
1 | 1 | 30000 | 6 |
2(デフォルト) | 1 | 20000 | 25 |
このトピックの詳細については、web.devをご覧ください。
内部化された非同期バンドル
#モジュールが同じバンドル内から同期および非同期の両方でインポートされる場合、個別のバンドルに分割するのではなく、非同期依存関係は「内部化」されます。つまり、重複を避けるために動的インポートと同じバンドル内に保持されますが、セマンティクスを維持するためにPromise
でラップされます。
このため、動的インポートは、依存関係が同期的に必要ないことを示す単なるヒントであり、新しいバンドルが作成されることを保証するものではありません。
重複排除
#動的にインポートされたモジュールに、可能なすべての祖先にすでに存在する依存関係がある場合、それは重複排除されます。たとえば、ページが動的にインポートされたモジュールでも使用されているライブラリをインポートする場合、ライブラリは実行時にすでにページに存在するため、親にのみ含まれます。
手動共有バンドル
#注:手動共有バンドルは現在実験段階であり、変更される可能性があります。
デフォルトでは、Parcelは一般的に使用されるモジュールを自動的に「共有バンドル」に分割し、上記にリストされたシナリオでバンドルを作成します。ただし、特定の場合には、バンドルに何が含まれるか、誰がそのバンドルをリクエストできるかを正確に指定したい場合があります。
これらのシナリオには、以下が含まれますが、これらに限定されません...
- 別のビルドツールまたはバンドラーからParcelへの設定の移植
- アセットを複製せずにHTTPリクエストを減らし、オーバーフェッチを優先する
- オーバーフェッチと全体的に少ないバンドルのロードは、特に非常に大規模なプロジェクトの場合、time-to-interactiveなどの測定値に役立ちます。
- 特定のルートまたはモジュールセット用に最適化された共有バンドルの作成
手動共有バンドルは、プロジェクトルートのpackage.json
で指定できます。assets
プロパティは、globのリストに設定する必要があります。これらのglobに一致する任意のアセットファイルパスは、手動共有バンドルに含まれます。
この例では、manual.js
から始まるグラフ内のすべてのJSアセットを含むベンダーバンドルを作成し、3つの並列HTTPリクエストに分割します。
{
"@parcel/bundler-default": {
"manualSharedBundles": [
{
"name": "vendor",
"root": "manual.js",
"assets": ["**/*"],
"types": ["js"],
"split": 3
},
],
},
}
オプションの完全なリストは次のとおりです。
- name(オプション)-バンドルの
manualSharedBundle
フィールドを<name>に設定します。これは、開発目的でカスタムレポーターまたはネーマーで読み取ることができます。 - root(オプション)-globの範囲を指定されたファイルに絞り込みます。上記の例では、globの
**/*
はmanual.js
内のすべてのインポートに一致します。 - assets(必須)-Parcelが一致するglob。globに一致するファイルは、単一のバンドルに配置され、特に指定がない限り、プロジェクト全体で重複排除されます。
root
が指定されていない場合、Parcelはglobをグローバルに一致させようとします。 - types(オプション)-globを特定のタイプに一致させる場合にのみ制限します。このフィールドは、
root
ファイルが複数のタイプ(JSやCSSなど)をインポートする場合、またはassets
globが異なるタイプに一致する可能性がある場合に設定する必要があります。バンドルには、同じタイプのアセットのみを含めることができます。- rootファイルには、複数のタイプのインポートを含めることができます。タイプごとに
manualSharedBundle
配列にオブジェクトを追加してください。
- rootファイルには、複数のタイプのインポートを含めることができます。タイプごとに
- split(オプション)-手動バンドルをx個のバンドルに分割します。
- 非常に大きなバンドルの場合、複数の並列HTTPリクエストに分割すると、CHR(キャッシュヒット率)などの測定値が改善される可能性があります。変更された場合、より小さなバンドルが無効になるためです。
注意してください!
手動共有バンドルを構成すると、Parcelによって通常実行されるすべての自動コード分割がオーバーライドされ、node_modules
を含むコードベース全体にマップされるため、意図しないロード順序の問題が発生する可能性があります。使用するglobに注意し、ファイルタイプごとに1つのバンドルのみを指定することをお勧めします。また、rootファイルを指定することをお勧めします。