Astro islands で初回 JS をゼロに近づける

「非同期読み込み」を「必要な時だけハイドレート」まで分解し、3層の遅延読み込みがそれぞれ何を解決するのかを実コードで説明します。

「非同期で読み込む」という言い方は曖昧です。少なくとも3つの別のことを指し得ますし、得られる効果は桁違いに違います。

第1層:ルート単位のコード分割

もっとも基本の層です。バンドラがページごとにチャンクを切り、/ja/games/snake を開いたときにダウンロードされるのは蛇のコードだけで、2048 は付いてきません。

Vite なら import.meta.glob で足ります。

const loaders = {
  ...import.meta.glob('../games/*/App.vue'),
  ...import.meta.glob('../tools/*/App.vue'),
};

要点はこの glob が静的に解析できることです。パスを変数で組み立てるなど動的にすると、バンドラは分割を諦めて全部を1つにまとめてしまいます。

第2層:ハイドレーションの遅延

この層は Astro 固有で、ドキュメント系フレームワークに対する本当の強みです。

VitePress では defineAsyncComponentコンポーネントの描画時に読み込みを始めます。一覧ページにカードを20枚置けば、20個のチャンクが同時に飛びます。Astro ではこう書けます。

<GameShell client:visible />

client:visible の意味は「ビューポートに入るまでコードすらダウンロードしない」。ほかに client:idle(アイドル時)、client:media(メディアクエリ一致時)、client:only(SSR をスキップ)があります。

下の小さな部品は実際にこの経路を通ります。まだハイドレートされておらず、スクロールして見えた時点で読み込まれます。

7x2 = 14

第3層:実行時のオンデマンド

最初の2層は「いつ落とすか」の話でした。この層は「そもそも落とすか」の話です。

ゲームエンジン、チャートライブラリ、Web Worker 内のデコーダ。これらは初回バンドルに入るべきではありません。JSON ツールの「差分」モードがまさにこの作りで、ボタンを実際に押したときだけ差分アルゴリズムを読み込みます。

const mod = await import('./logic/diff');
diffEntries.value = mod.diffValues(left.value, right.value);

代償

3層を合わせると、コンテンツページはほぼ JS ゼロ、アプリページはアプリ単位で分割、重い依存は操作単位で分割されます。

代わりにビルド設定と、3つの可動部からなるメンタルモデルが増えます。SSG・islands・クライアント側マウントを同時に理解していなければなりません。記事とゲームが同じサイトに同居するなら、この代償は払う価値があると考えています。

← 記事一覧に戻る

コメント