Back

Svelteでコンポーネントを遅延ロードする方法

Svelteでコンポーネントを遅延ロードする方法

Svelteアプリはデフォルトで高速ですが、リッチテキストエディタ、チャートライブラリ、または複雑なダッシュボードウィジェットを初期JavaScriptペイロードにバンドルしている場合、ユーザーは使用しない可能性のあるコードをダウンロードしています。Svelteのコンポーネントレベルの遅延ロードは、これらの重いインポートを実際に必要になるまで延期することで、この問題を解決します。

Reactとは異なり、Svelteには組み込みのlazy()ヘルパーがありません。代わりに、遅延ロードはJavaScriptのネイティブなimport()関数と条件付きレンダリングを直接組み合わせて使用します。より手動的ですが、より柔軟性があります。

重要なポイント

  • Svelteには組み込みのlazy()ヘルパーがないため、遅延ロードはJavaScriptのネイティブなimport()と条件付きレンダリングを使用します
  • SvelteKitはルートレベルのコード分割を自動的に処理しますが、単一ページ内の重いウィジェットにはコンポーネントレベルの遅延ロードが依然として必要です
  • Viteは設定不要で動的インポートを個別のチャンクに分割します
  • 3つのコアパターンでほとんどのユースケースをカバーできます:{#await}を使用したオンデマンドロード、ホバー時のプリロード、ブラウザがアイドル状態になるまで延期

SvelteKitでコンポーネントレベルの遅延ロードが依然として重要な理由

SvelteKitはすでにルートレベルのコード分割を自動的に処理しています。各ルートは独自のチャンクを取得するため、ページ間を移動しても初期バンドルが肥大化しません。しかし、単一ページ内では、.svelteファイルの先頭でインポートしたすべてのものが一緒にバンドルされ、最初にロードされます。

ここでコンポーネントレベルの遅延ロードが価値を持ちます。ページに重いチャートコンポーネント、ビデオプレーヤー、またはユーザーの操作後にのみ表示されるモーダルが含まれている場合、ページ到着時にそのコードをロードする理由はありません。

SvelteKitはViteでビルドされ、動的インポートをネイティブに処理します。Viteがimport('./HeavyChart.svelte')を見ると、自動的にそのモジュールを個別のチャンクに分割します。追加の設定は不要です。

Svelteコンポーネントを遅延ロードする方法:コアパターン

{#await}を使用した基本的な遅延ロード

最もシンプルなアプローチは、テンプレート内でSvelteの{#await}ブロックを直接使用します:

<!-- src/routes/+page.svelte -->
<script>
  let showChart = false;
  let chartData = [1, 2, 3];
</script>

<button onclick={() => showChart = true}>Load Chart</button>

{#if showChart}
  {#await import('$lib/components/HeavyChart.svelte')}
    <p>Loading chart...</p>
  {:then Chart}
    <Chart.default data={chartData} />
  {:catch error}
    <p>Failed to load chart: {error.message}</p>
  {/await}
{/if}

これはSvelte 4とSvelte 5の両方で動作します。{:catch}ブロックは重要です — ネットワーク障害は発生するものであり、サイレントエラーはデバッグを困難にします。Svelte 4では、動的にコンポーネントを切り替える場合、通常<svelte:component this={Component}>を使用します。モダンなSvelteでは、コンポーネント参照を変更することで直接再レンダリングをトリガーすることもできます。

ブラウザは最初のインポート後にモジュールをキャッシュします。同じコンポーネントの後続のレンダリングでは、新しいネットワークリクエストはトリガーされません。

より速い体感パフォーマンスのためのホバー時のロード

一般的なパターンは、ユーザーが意図を示したとき — トリガー要素にホバーしたときにロードを開始することです:

<!-- src/routes/+page.svelte -->
<script lang="ts">
  import type { Component } from 'svelte';

  let HeavyWidget: Component<{ message: string }> | null = $state(null);

  async function loadWidget() {
    if (!HeavyWidget) {
      const module = await import('$lib/components/HeavyWidget.svelte');
      HeavyWidget = module.default;
    }
  }
</script>

<div onmouseenter={loadWidget}>
  <p>Hover to preload the widget</p>
</div>

{#if HeavyWidget}
  <HeavyWidget message="Ready!" />
{/if}

これはツールチップ、ポップオーバー、サイドバーに適しています。コンポーネントはユーザーがクリックする前にロードを開始するため、ユーザーが操作する頃にはすでにキャッシュされていることが多いです。

ブラウザがアイドル状態のときにロード

ページを改善するが即座に必要ではない非クリティカルなコンポーネントには、requestIdleCallbackを使用します:

<script lang="ts">
  import { onMount } from 'svelte';
  import type { Component } from 'svelte';

  let FeedbackWidget: Component | null = $state(null);

  onMount(() => {
    const load = () =>
      import('$lib/components/FeedbackWidget.svelte').then(
        (m) => (FeedbackWidget = m.default)
      );

    if ('requestIdleCallback' in window) {
      requestIdleCallback(load);
    } else {
      setTimeout(load, 300); // Safariのフォールバック
    }
  });
</script>

{#if FeedbackWidget}
  <FeedbackWidget />
{/if}

SafariのrequestIdleCallbackサポートは歴史的に一貫性がないため、setTimeoutフォールバックが推奨されます。

遅延ロードすべきでない場合

すべてのコンポーネントが遅延ロードの恩恵を受けるわけではありません。以下の場合は避けてください:

  • ファーストビューのUI — ナビゲーション、ヒーローセクション、主要コンテンツ
  • 小さなコンポーネント — 非同期のオーバーヘッドが節約分を上回る
  • マウント時に即座に必要なコンポーネント — ロードのフラッシュがUXを低下させる

過度の分割は多くの小さな非同期チャンクを作成します。Viteのようなモダンなバンドラーはこれを効率的に処理しますが、それでも収穫逓減のポイントがあります。

まとめ

Svelteコンポーネントの遅延ロードは一つのことに集約されます:静的インポートの代わりにimport()を使用し、結果を条件付きでレンダリングすることです。SvelteKitのViteベースのビルドは、コード分割を自動的に処理します。上記の3つのパターン — オンデマンド、ホバー時、アイドル時 — は、ほとんどの実際のシナリオをカバーします。ユーザーが実際にコンポーネントを必要とするタイミングに合わせてトリガーを選択し、エラー処理を追加すれば、初期バンドルは軽量に保たれます。

よくある質問

動的インポートは両方のバージョンで動作します。Svelte 4では、通常、ロードされたコンポーネントをthis属性を持つsvelte:componentを使用してレンダリングします。Svelte 5では、解決されたコンポーネントをテンプレート内のタグとして直接使用できます。基礎となるインポートメカニズムは両方のケースで同じです。

Node.jsが動的インポートをサポートしているため、SSR中にも動的インポートは実行されます。ただし、遅延ロードは主にブラウザの初期JavaScriptペイロードを削減することを目的としたクライアントサイドの最適化です。コンポーネントがSEOやファーストペイントの理由でサーバー上でレンダリングされる必要がある場合は、静的インポートがより良い選択です。

ビルド出力を確認するか、バンドルアナライザーを使用して大きなチャンクを特定してください。チャートレンダラー、リッチテキストエディタ、マップウィジェットなどの重いサードパーティライブラリを取り込むコンポーネントは強力な候補です。コンポーネントが数キロバイト未満しか追加しない場合、追加のネットワークリクエストのオーバーヘッドが節約分を上回る可能性があります。

いいえ。ブラウザは最初の動的インポートが解決された後、モジュールをキャッシュします。後続のレンダリングでは、キャッシュされたモジュールが即座に返されるため、ローディング状態は初回のフェッチ時にのみ表示されます。また、ホバー時やアイドル時にモジュールをプリロードして、可視的な遅延を完全に排除することもできます。

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay