チュートリアル: JavaScript

Islands の基本

Islands アーキテクチャは部分的なハイドレーションを可能にします - ページのインタラクティブな部分のみが JavaScript をロードします。

問題

従来の SPA はページ全体に JavaScript を送信します:

┌─────────────────────────────────────┐
│  ヘッダー(静的)     ← JS ロード   │
├─────────────────────────────────────┤
│  記事(静的)         ← JS ロード   │
│                                     │
│  コメント(インタラクティブ)← JS 必要 │
│                                     │
│  フッター(静的)     ← JS ロード   │
└─────────────────────────────────────┘

結果:大きなバンドル、遅いロード、無駄なリソース。

解決策

Islands はインタラクティブなコンポーネントのみをハイドレート:

┌─────────────────────────────────────┐
│  ヘッダー(静的)     ← JS なし     │
├─────────────────────────────────────┤
│  記事(静的)         ← JS なし     │
│                                     │
│  ┌─────────────────────────────┐    │
│  │ コメント Island    ← JS     │    │
│  └─────────────────────────────┘    │
│                                     │
│  フッター(静的)     ← JS なし     │
└─────────────────────────────────────┘

結果:最小限の JavaScript、高速ロード、優れた Core Web Vitals。

Island の作成

1. サーバーレンダリングされた HTML

サーバーがハイドレーション属性を持つ Island を Custom Element として出力:

<wc-counter
  luna:wc-url="/static/wc-counter.js"
  luna:wc-state="0"
  luna:wc-trigger="load"
>
  <template shadowrootmode="open">
    <button>Count: 0</button>
  </template>
</wc-counter>

MoonBit でのサーバーサイドレンダリングについては、MoonBit チュートリアルを参照してください。

2. クライアントサイド(TypeScript)

インタラクティブコンポーネントを作成:

// wc-counter.ts
import { createSignal, hydrateWC } from '@luna_ui/luna';

interface CounterProps {
  initial: number;
}

function Counter(props: CounterProps) {
  const [count, setCount] = createSignal(props.initial);

  return (
    <>
      <style>{`:host { display: block; }`}</style>
      <button onClick={() => setCount(c => c + 1)}>
        Count: {count()}
      </button>
    </>
  );
}

// ハイドレーションに登録
hydrateWC("wc-counter", Counter);

3. ハイドレーション

  1. ページがサーバーレンダリングされた HTML でロード(即座に表示)

  2. Luna ローダーが [luna:wc-url] 要素を検出

  3. トリガーに基づいて /static/wc-counter.js をロード

  4. JavaScript が引き継ぎ、要素がインタラクティブに

Island 属性

属性目的
luna:wc-urlコンポーネント JavaScript をロードする URL
luna:wc-stateシリアライズされた props(JSON)
luna:wc-triggerいつハイドレートするか

customElements.define()不要 です。 loader は [luna:wc-url] を持つ要素を直接スキャンするので、 Custom Element として登録していないタグでも動作します。

複数の Islands

各 Island は独立しています。典型的なページ構造:

<div>
  <h1>マイページ</h1>

  <!-- 検索 Island - 即座にハイドレート -->
  <wc-search luna:wc-url="/wc-search.js" luna:wc-trigger="load">...</wc-search>

  <!-- 記事 - 純粋な HTML、JS なし -->
  <article>
    <p>静的コンテンツ...</p>
  </article>

  <!-- コメント Island - 表示時にハイドレート -->
  <wc-comments luna:wc-url="/wc-comments.js" luna:wc-trigger="visible">...</wc-comments>

  <!-- フッター - 純粋な HTML -->
  <footer>...</footer>
</div>

試してみよう

典型的なブログページを考えてください。どの部分を Island にしますか?

解答
ブログページ構造:
├── ヘッダー         → 静的(Island なし)
├── ナビゲーション   → 静的(Island なし)
├── 記事             → 静的(Island なし)
├── シェアボタン     → Island(クリック追跡)
├── コメントフォーム → Island(フォーム送信)
├── コメントリスト   → Island(ライブ更新)
├── 関連投稿         → 静的(Island なし)
└── フッター         → 静的(Island なし)

完全なインタラクティビティに必要なのは3つの Island だけ!

次へ

ハイドレーショントリガー → について学ぶ