初期化処理
ページ初期化処理の接続
ページを置き換えた後は、新しいページの island を hydrate し、ページ単位の初期化処理を実行する必要があります。HonoX との連携は、生成されたクライアントモジュールを通じてこの接続を提供します。
生成されるクライアントモジュール
HonoX 連携は virtual:riebeckite/client を提供します。spa が有効な場合、生成されたモジュールはランタイムを一度だけ開始します。
stopSpaNavigation ??= startSpaNavigation({
...spaConfig,
onBeforePageCommit: () => lifecycle.unmount("page"),
onPageCommit: async ({ root, signal }) => {
if (signal.aborted) return;
await createClient();
if (signal.aborted || !root.isConnected) return;
mountPageClientInitializers(root);
},
});
spaConfig はシリアライズされた設定オブジェクトです。このモジュールは、解決済みの spa 値がある場合だけ、honox/client の createClient と @riebeckite/spa の startSpaNavigation を読み込みます。
生成されたモジュールは、次の関数を公開します。
initRiebeckiteClient()— アプリケーションとページの初期化処理を実行し、SPA ナビゲーションを開始するremountRiebeckitePage(root)— ページスコープを unmount し、指定したルートに mount し直すdisposeRiebeckiteClient()— SPA ナビゲーションを停止し、両方のスコープを破棄する
通常のサイトコードが呼ぶのは initRiebeckiteClient() だけです。ほかの関数は、独自の連携処理と破棄のために用意されています。
初期化処理のライフサイクル
生成されたモジュールは、Plugin のクライアントエントリが持つメタデータに従って初期化処理を実行します。
scope: "app"はdocumentに一度だけ mount するscope: "page"(既定)はmain#main-contentに mount し、ページを置き換える前に unmount するinitializerArgumentsは引数の種類を選ぶ。config(既定)、root、contextのいずれか
生成されたコードでは、ClientLifecycle.mount を待たずに呼び出します(void lifecycle.mount(...))。そのため、非同期の初期化処理が次のページ移動の開始時点でも実行中の場合があります。初期化処理は signal を受け取り、中断されたら停止する必要があります。
同じ名前の初期化処理を同じスコープに mount した場合、ルートが同じなら既存の mount を再利用します。ルートが違う場合は、ライフサイクルが例外を投げます。
ClientLifecycle.unmount は、先にスコープを中断してから、クリーンアップの完了を待ちます。クリーンアップで起きたエラーは報告されますが、ほかの初期化処理の終了を妨げません。
生成された onPageCommit は、createClient() の完了後に、ページ移動が中断されていないことと、対象のページがまだ document に接続されていることを再確認してから、ページの初期化処理を実行します。これにより、hydration に時間がかかった場合でも、古いページの初期化処理を現在のページに対して実行してしまうことを防ぎます。
initializerArguments: "context" を指定したクライアントエントリは、{ root, config, signal, scope } を受け取ります。scope は、イベントリスナー、タイムアウト、インターバル、独自のクリーンアップを管理します。グローバル環境を書き換えたり、初期化処理の外で作られたリソースを推測したりはしません。必要なリソースを明示的に登録する仕組みです。
ビルド結果への影響
SPA ランタイムが変えるのはクライアントバンドルであり、サーバー側のレンダラーやSSGの出力ではありません。
spaを省略するかfalseにした場合、仮想モジュールは@riebeckite/spaもhonox/clientも読み込みません。ランタイムはバンドルに含まれません。spaを有効にした場合、Vite はランタイムを含め、生成されたクライアントモジュールにcreateClientの読み込みと SPA ナビゲーションの開始処理を追加します。- サーバービルドと静的出力の計画は変わりません。どちらの場合も同じ HTML を描画します。
外部の fixture で測定した結果(2026-10-21)では、SPA を有効にすると、重複を除いた JavaScript アセットが raw で5,570 B、gzip で1,715 B、brotli で1,472 B 増えました。同じローカルの no-store fixture では、初回読み込みの中央値は実質的に変わらず、hover による先読みは、先読みなしの SPA ナビゲーションの中央値を短縮しました。これらの数値は、その fixture に固有のものです。
検証範囲
現在、自動テストで確認している範囲は次のとおりです。
packages/spa/test/spa.test.tsの設定値の検証と、packages/spa/test/prefetch.test.tsのキャッシュの有効期限(TTL)、重複排除、LRU による削除、呼び出し元ごとの中断の分離packages/integrations/honox/test/client_module.test.tsの生成された import 文、app・page・context引数、SPA の有効・無効の接続tests/spa-runtime/browser.test.tsのブラウザ上でのページ移動の競合、head の所有権、履歴、ハッシュへのフォーカス、フォールバック、リダイレクト、先読み済みページの再利用
このリポジトリのブラウザテストでは、次の範囲を網羅していません。
- クリック判定の境界的なケース
popstate時のスクロール位置の復元- 実際の Plugin 初期化処理でのクリーンアップ失敗
これらの領域を変更するときは、対象を絞ったテストか手動での確認が必要です。テストがないことは、動作が完成している証拠ではありません。