SvelteKitのレンダリングとハイドレーションを徹底解説!SSR・CSRとの違いとは?
大平 恵美
大平 恵美
2025-02-12
SvelteKitのレンダリングは、SSR(サーバーサイドレンダリング)でページを高速表示し、ハイドレーションによってクライアント側でインタラクティブにする仕組みです。本記事では、SSR・CSRとの違い、ハイドレーションの役割、データの保存方法まで詳しく解説します。SvelteKitの強みを最大限に活かすための基礎をしっかり学びましょう!
あなた:

私は+page.svelteにコードを書いています。書いたコードがapp.htmlに反映されます。レンダリングはどの部分で行われているかイメージがつきません。

ChatGPT:

SvelteKitのレンダリングの流れが掴みづらいということですね!
ここでは、SvelteKitがどのように+page.svelteのコードを処理し、app.htmlに反映するのか を詳しく説明します。


SvelteKitのレンダリングの流れ

SvelteKitでは、+page.svelte のコードがどのように処理され、ブラウザに表示されるのかを分解すると、次のような流れになります。

1. ユーザーがページにアクセス

  • 例えば、https://example.com/ にアクセスしたとします。
  • このURLに対応する +page.svelte のコードが実行されます。

2. SSR(サーバーサイドレンダリング)でHTMLを生成

  • サーバーが +page.svelte を解析し、HTMLを生成
  • この時点では onMount は実行されない
  • 生成されたHTMLは app.html に組み込まれ、クライアント(ブラウザ)に送信される

📌 この処理のイメージ

ユーザーがURLにアクセス  →  サーバーが `+page.svelte` をレンダリング  →  HTMLを生成しクライアントに送信

サーバー側の出力

<!DOCTYPE html>
<html lang="ja">
<head>
  <title>My App</title>
</head>
<body>
  <div id="svelte"> <!-- ここにSSRされたHTMLが挿入される -->
    <h1>こんにちは、世界!</h1>
  </div>
</body>
</html>

これは SSRによってサーバー側で生成されたHTML です。


3. クライアントサイドでハイドレーション(ブラウザでの再レンダリング)

  • クライアントにHTMLが送られた後、SvelteKitは 「ハイドレーション(hydration)」 という処理を行い、+page.svelte のSvelteコンポーネントを有効化します。
  • ここで初めて onMount やクライアント側のコードが実行される

📌 この処理のイメージ

サーバーからHTMLを受け取る  →  クライアントでSvelteのJSが実行される  →  `onMount` やインタラクティブな処理が動く

この時点でのブラウザの動作

<script>
  import { onMount } from 'svelte';

  let message = "こんにちは、世界!";

  onMount(() => {
    console.log("このコードはブラウザでのみ実行される");
  });
</script>

<h1>{message}</h1>
  • onMount が動き、ブラウザコンソールに "このコードはブラウザでのみ実行される" と表示される。

4. ユーザー操作による再レンダリング

  • ユーザーがボタンを押すなどして状態が変わると、クライアントサイドでのみ再レンダリング が行われる(この時、サーバーは関与しない)。

例えば、ボタンを押すとカウントが増えるコード

<script>
  let count = 0;
</script>

<button on:click={() => count++}>カウント: {count}</button>
  • ボタンを押すと、クライアントサイドでのレンダリングが発生
  • このレンダリングはSSRとは無関係にブラウザだけで処理される

SvelteKitのレンダリングの流れをまとめると

ステップ 説明 どこで実行されるか
1. ユーザーがURLにアクセス +page.svelte を処理 サーバー
2. SSRがHTMLを生成 app.html に組み込まれ、送信される サーバー
3. クライアントでハイドレーション onMount などが実行される ブラウザ
4. ユーザー操作による再レンダリング 状態の変化によってUIが更新される ブラウザ

app.html の役割

  • app.htmlSvelteKitがSSRで生成したHTMLを挿入するテンプレート
  • <div id="svelte"> の部分に +page.svelte のレンダリング結果が埋め込まれる

📌 app.html のサンプル

<!DOCTYPE html>
<html lang="ja">
<head>
  <title>SvelteKit App</title>
</head>
<body>
  <div id="svelte"><!-- ここに `+page.svelte` のHTMLが入る --></div>
  <script type="module" src="/_app/entry.js"></script> <!-- クライアント用JS -->
</body>
</html>
  • +page.svelte の内容が <div id="svelte"> に差し込まれる。
  • <script type="module" src="/_app/entry.js"> によってクライアント側のSvelteアプリが実行される。

結論

📌 SvelteKitのレンダリングのイメージ

  1. SSRで +page.svelte のHTMLを生成し、app.html に埋め込む
  2. ブラウザでハイドレーションが行われ、Svelteコンポーネントが有効化
  3. その後、ユーザー操作によってクライアント側でのみレンダリングが発生

SSR時にはサーバーでHTMLが生成される
クライアントサイドでは onMount などの処理が実行される
ユーザー操作によるUIの変更はブラウザ上でレンダリングされる

これで SvelteKitのレンダリングの流れ がイメージしやすくなると思います! 🚀

あなた:

ハイドレーションが分かりません。混乱するので分かりやすく教えてください。

ChatGPT:

ハイドレーション(Hydration)とは?

簡単に言うと、「サーバーが生成した静的なHTMLを、クライアント(ブラウザ)でインタラクティブにするプロセス」 です。


1. ハイドレーションを身近な例で理解する

ハイドレーションを レゴブロックに例えてみましょう。

  1. SSR(サーバーサイドレンダリング)

    • あなたがレゴセット(アプリ)を買ったとします。
    • お店(サーバー)は すでに完成したレゴの建物(HTML)を作って あなたに渡します。
    • これが SSR です。(ブラウザが何もせずに最初から表示できる)
  2. ハイドレーション

    • でも、このレゴの建物はただの飾りで、まだ動きません。
    • そこで、あなたがレゴのパーツを動かせるようにする(ボタンを押せるようにする)ために、組み立て直します。
    • これが ハイドレーション です。(クライアントがJSを読み込み、インタラクティブにする)

2. SvelteKitにおけるハイドレーションの流れ

SvelteKitでは、次のようなステップで SSRとハイドレーションが行われます。

📌 ステップ 1: サーバーが静的なHTMLを返す

ユーザーがページにアクセスすると、SvelteKitのサーバーは +page.svelte を処理し、HTMLを生成 します。

<!DOCTYPE html>
<html lang="ja">
<head>
  <title>My App</title>
</head>
<body>
  <div id="svelte">
    <button>カウント: 0</button> <!-- ただの静的HTML -->
  </div>
</body>
</html>

🔹 この時点では、ボタンを押しても何も起こりません。
🔹 なぜなら、まだ JavaScript が動いていないから!


📌 ステップ 2: ブラウザが entry.js を実行(ハイドレーション開始)

  • ブラウザが app.html に含まれる <script> を実行
  • これにより、SvelteKitのクライアントサイドのコード(JS)がロードされる
  • Svelteのコンポーネントが有効化され、ボタンが動くようになる
<script>
  let count = 0;
</script>

<button on:click={() => count++}>カウント: {count}</button>

➡ この時点で、ボタンを押すとカウントが増えるようになる!


3. まとめ:ハイドレーションとは?

📌 サーバーが生成した静的なHTMLに、クライアントサイドでSvelteの機能を適用するプロセス。
📌 これにより、ブラウザ上でボタンが動くようになる。


4. ハイドレーションがないとどうなる?

もし ハイドレーションが行われなかったら どうなるでしょう?

  1. ページは表示される(SSRのHTMLはある)
  2. でも、ボタンを押しても何も動かない
  3. 理由 → JSが実行されていないので、ボタンの on:click が無効

📌 つまり、ハイドレーションは「静的なHTMLをインタラクティブにする魔法」 なのです! ✨


5. ハイドレーションの利点

SSRによる高速な初回表示(最初からHTMLがあるので、画面がすぐ見える)
クライアントサイドの動作も可能(ボタンなどが動くようになる)
SEOやパフォーマンスの向上(検索エンジンがページの内容を読み取れる)


6. ハイドレーションをイメージする図

【SSRの段階】 サーバーからブラウザへ
┌──────────┐
│  <button>カウント: 0</button>  │ ← ただのHTML
└──────────┘

【ハイドレーション】 JavaScriptが動く
┌──────────┐
│  <button onClick="動作">カウント: 0</button>  │ ← JSが適用される
└──────────┘

【インタラクティブ】 ユーザーが操作
┌──────────┐
│  <button>カウント: 1</button>  │ ← ボタンを押すと更新される
└──────────┘

7. 結論

❓「ハイドレーションって何?」
💡 サーバーが生成したHTMLを、クライアントでインタラクティブにする処理のこと!

❓「なぜ必要なの?」
💡 SSRだけだとボタンが動かないから、JSを使ってインタラクティブにするため!

❓「どうやって行われるの?」
💡 SvelteKitの entry.js(クライアントサイドのJS)がロードされることで実行される!


これで、ハイドレーションの仕組みがスッキリ理解できたはず! 🚀

あなた:

ハイドレーションの具体例を教えてください。

ChatGPT:

🚀 ハイドレーションの具体例を見てみよう!

ハイドレーションの仕組みを コードと動作の違い を通して、もっと明確に理解できるようにしよう!
ここでは SSR(サーバーサイドレンダリング)だけの状態ハイドレーションが完了した後の状態 を比較するよ。


📝 例1: カウンター

カウンターのボタンを押すと、数値が増えるシンプルな例


① SSR(サーバーで生成されるHTML)

ユーザーが https://example.com にアクセスした瞬間、サーバーが +page.svelte を処理して、HTMLを生成 する。

この時点でのHTML(ただの静的HTML)

<!DOCTYPE html>
<html lang="ja">
<head>
  <title>My App</title>
</head>
<body>
  <div id="svelte">
    <button>カウント: 0</button> <!-- ただのHTML、クリックしても何も起きない -->
  </div>
</body>
</html>

📌 この時点では…

  • ページは表示される
  • でも、ボタンを押しても何も変わらない!(JSが動いてないから)

② ハイドレーション(クライアントでJSが動く!)

ページのHTMLがブラウザに表示された後、SvelteKitのクライアントJSが読み込まれて、ハイドレーションが行われる!

📌 この時点で何が起こる?

  • SvelteのクライアントJSが +page.svelte のロジックをブラウザ上で適用する
  • onMount などが動作し始める
  • ボタンがクリックできるようになる!

実際の +page.svelte のコード

<script>
  let count = 0;
</script>

<button on:click={() => count++}>カウント: {count}</button>

ハイドレーションが完了すると…

  • count の値が Svelte によって管理されるようになる
  • on:click イベントが動くようになる
  • ボタンを押すと、カウントが増える! 🎉

📝 例2: フォームの入力フィールド

次に、ユーザーが入力できるフォーム を使った例を見てみよう!


① SSR時の静的なHTML

ページを開いたとき、サーバーが次のHTMLを生成する。

<!DOCTYPE html>
<html lang="ja">
<head>
  <title>My App</title>
</head>
<body>
  <div id="svelte">
    <input type="text" placeholder="名前を入力" />
  </div>
</body>
</html>

📌 この時点では…

  • 入力欄はあるが、JavaScriptが動いていない
  • 例えば「リアルタイムで入力した内容を表示する」といった動作はできない

② ハイドレーションが完了すると…

SvelteのJSが動き始めて、インタラクティブな動作が可能になる。

実際の +page.svelte のコード

<script>
  let name = "";
</script>

<input type="text" bind:value={name} placeholder="名前を入力" />
<p>あなたの名前: {name}</p>

ハイドレーション後にできること

  • 入力した内容が name 変数にバインドされる
  • リアルタイムで <p> に入力した文字が反映される! 📝

📝 例3: APIからデータを取得する

次は、APIからデータを取得する場合のハイドレーションの動き を見てみよう。


① SSR時の静的なHTML

ページを開いた瞬間に、サーバーがデータを取得してHTMLを生成する場合:

<!DOCTYPE html>
<html lang="ja">
<head>
  <title>My App</title>
</head>
<body>
  <div id="svelte">
    <ul>
      <li>Loading...</li> <!-- まだデータがない -->
    </ul>
  </div>
</body>
</html>

📌 この時点では…

  • fetchonMount の中で実行されるため、SSR時にはデータが取得されていない
  • Loading... しか表示されない

② ハイドレーションが完了し、データを取得

+page.svelte のコード:

<script>
  import { onMount } from "svelte";
  let items = [];

  onMount(async () => {
    const res = await fetch("https://jsonplaceholder.typicode.com/todos?_limit=5");
    items = await res.json();
  });
</script>

<ul>
  {#each items as item}
    <li>{item.title}</li>
  {/each}
</ul>

ハイドレーション後の動作

  • onMount が動き、データ取得が始まる
  • 取得が完了すると、 items にデータが入る
  • 画面が更新され、実際のデータが表示される!

📌 ハイドレーションが行われる前後の違い

状態 SSR時の動作 ハイドレーション後
カウンター ボタンは表示されるが、押しても何も起こらない count が更新され、ボタンが動く
フォーム 入力欄はあるが、入力した内容がリアルタイムで反映されない bind:value によって入力値が動的に変わる
APIデータ Loading... のまま onMount でデータを取得し、リストが更新される

🎯 まとめ

💡 ハイドレーションとは?
サーバーが生成した静的なHTMLを、ブラウザ側で動くようにする処理!

💡 なぜ必要?
➡ SSRだけではページは「見える」けど「動かない」から!

💡 どんな動作が変わる?
✅ ボタンが押せるようになる
✅ 入力フォームがリアルタイムで更新される
✅ APIデータが表示される


これで ハイドレーションの実例 が掴めたんじゃないかな?💡
「もうちょっと詳しく知りたい!」とか、「他の例も見たい!」があれば教えてね!🚀

あなた:

どこかにクリックしたことや入力内容が保存されるのですか?分かりやすく簡潔に教えてください。

ChatGPT:

💡 クリックや入力データはどこに保存されるのか?

データが保存される場所は 「どう扱うか」 によって違うよ!


1️⃣ コンポーネントの変数(ページをリロードすると消える)

<script>
  let count = 0;
</script>

<button on:click={() => count++}>カウント: {count}</button>

📌 どこに保存?ブラウザのメモリだけ(リロードすると 0 に戻る)


2️⃣ localStorage(リロードしてもデータが残る)

<script>
  import { onMount } from "svelte";

  let count = 0;

  onMount(() => {
    count = Number(localStorage.getItem("count")) || 0;
  });

  function increment() {
    count++;
    localStorage.setItem("count", count);
  }
</script>

<button on:click={increment}>カウント: {count}</button>

📌 どこに保存?ブラウザのlocalStorage(リロードしても残る)


3️⃣ Svelteの store(ページ移動してもデータが保持される)

// stores.js
import { writable } from "svelte/store";
export const count = writable(0);
// +page.svelte
<script>
  import { count } from "../stores.js";
</script>

<button on:click={() => count.update(n => n + 1)}>カウント: {$count}</button>

📌 どこに保存?Svelteの store(リロードすると消えるが、ページを移動しても保持される)


4️⃣ サーバーのセッション(ログイン情報などを保存)

// +page.server.js
export function load({ cookies }) {
  return { count: Number(cookies.get("count") || 0) };
}

export const actions = {
  increment: async ({ cookies }) => {
    let count = Number(cookies.get("count") || 0);
    count++;
    cookies.set("count", count.toString());
  },
};
// +page.svelte
<script>
  export let count;
</script>

<form method="POST" action="?/increment">
  <button type="submit">カウント: {count}</button>
</form>

📌 どこに保存?サーバーの cookies(リロードしてもデータが保持される!)


🔍 どの方法を使えばいい?

方法 リロード後のデータ ページ移動後のデータ 使う場面
コンポーネントの変数 消える 消える 一時的なデータ
localStorage 保持される 保持される リロード後も残したいデータ
store (writable) 消える 保持される アプリ全体で共有したいデータ
サーバーセッション 保持される 保持される ユーザーごとの情報(ログインなど)

📌 まとめ ✅ ただの変数はリロードで 消える
localStorage に保存すれば リロードしても保持される
store を使うと ページ移動してもデータが保持される
✅ サーバー側で cookies を使えば ユーザーごとにデータを管理できる


「この場合はどうする?」みたいな具体的な例があれば教えてね!🚀