コンテンツにスキップ
svelte-vitals
Esc
↑↓移動↵開く⌘Jプレビュー
このページの内容

performance/load-waterfall · Load waterfall

universal load で依存関係のある await が数珠つなぎになると、1 ホップごとにブラウザからのネットワーク往復が発生します。

重大度: warning · カテゴリ: performance

チェック内容

universal load(+page.ts / +layout.ts)内で、後続の await が先行する await の結果を使っているチェーン(直接参照、分割代入した束縛、中間定数経由を含む)を検出します。クライアントサイドナビゲーションでは、依存ホップ1つごとにブラウザからの完全なネットワーク往復が発生します。

走査は意図的に保守的です。

  • load ボディの直線状のステートメント(直接 try で包まれたものを含む)だけを追い、if 分岐・ループ・ネストした関数には入りません。
  • await parent() 自体は対象外ですが、そこから得たデータは依存としてカウントされます。
  • レスポンスボディの読み取り(await res.json() など。そうした読み取りだけを並べた Promise.all も含みます)は追加の往復を要しないためホップに数えませんが、そこから得たデータは依存として伝播します。
  • load 内で作ったオブジェクトのメソッドを結果を使わずに await している場合(const auth = getAuth(); await auth.initialize())は、そのオブジェクトを埋める処理と読みます。後でそのオブジェクトを読む await は、それに依存します。
  • 以前の結果を反復するコールバックの中で埋めたコンテナ(lists.forEach((l) => ids.add(l.id)))は、その結果を引き継ぎます。後でそのコンテナを読む await は、それに依存します。
  • 先行する結果が渡してきた Promise の await もホップに数えません。たとえば祖先の load がすでに開始し、parent() 経由で渡された Promise(await deferred.state)です。その時点では新しいリクエストが始まらないためです。一方、この load が先行する結果から開始した Promise(const p = fetch(user.url) の後の await p)はホップに数えます。
  • server load 内の依存チェーンは対象外です。並列化は不可能で、すでにサーバーサイドで実行されているためです。
  • クライアントサイドレンダリングが無効なルートの load も対象外です。クライアントランタイムがないため、universal load は SSR 時にしか実行されません。SvelteKit と同じく、ページ自身の +page ファイル、続いてレイアウトチェーンで最も近い csr の export を採用します。csr = false と csr = dev(dev は $app/environment のもので、本番ビルドでは必ず false)で無効になり、それ以外の csr の export で再び有効になります。同じ階層の universal モジュールと server モジュールの両方が csr を export している場合は universal 側を採用します。レイアウトの load が対象外になるのは、その下のすべてのページで csr が無効な場合だけです。

なぜ重要か

SvelteKit のパフォーマンスガイドは、リクエストウォーターフォールを主要なレイテンシ源として挙げています。universal load はクライアントサイドナビゲーションのたびにブラウザで再実行されるため、N 個の依存リクエストのチェーンは毎回 N 回の逐次往復を要します。チェーンを server load に移せばロジックはそのままに、ホップはサーバー間通信となり、クライアントのコストは1往復に収まります。

修正方法

依存チェーンを server load に移動します。

export async function load({ fetch }) {
  const user = await fetch(`/api/user`).then((r) => r.json());
  const posts = await fetch(`/api/posts/${user.id}`).then((r) => r.json());
  return { user, posts };
}

一部のデータが独立している場合は、切り出して並列化してください(performance/sequential-awaits を参照)。

制限事項

検出できるのは、依存チェーンがそのままの形で書かれている場合だけです。分岐、ループ、ヘルパー関数、モジュールレベルのキャッシュの背後に隠れたチェーンは検出できません。特定の行だけ抑制するには // svelte-vitals-disable-next-line performance/load-waterfall を使います。

モードによる違い

ありません。このルールが読むのは同じ .svelte / .ts のソースファイルなので、CLI、Vite プラグインのビルド、ライブダッシュボードの静的ベースラインのいずれでも結果は同一で、レンダリング済み HTML の解析で再評価されることもありません。--route で実行範囲を絞ると、このルールは動きません。コンポーネントスコープのルールには、検出を紐づけるルートが無いためです。

無効化

export default {
  rules: {
    'performance/load-waterfall': 'off'
  }
};