スポンサーリンク
スポンサーリンク
1️⃣ はじめに:なぜSvelteKitが来るのか
JavaScriptフレームワーク戦国時代、ReactやVueの次に脚光を浴びるのがSvelteKitです。従来のSSRとSPAは相反するアーキテクチャとされていましたが、SvelteKitはその両立を軽量かつ高速に実現します。
- バンドル前コンパイル:余計なランタイムが不要
- ファイルベースルーティング:設定不要で簡潔
- load関数:組み込みでSSRデータフェッチ
国内情報はまだ少なく、アーリーアダプター向けの先行者利益を狙える技術です。
2️⃣ 開発環境の準備
必要なもの
- Node.js 16以上
- Git
- 任意のコードエディタ (VSCode推奨)
SvelteKit プロジェクト作成
npm init svelte@next my-app
cd my-app
npm install
npm install -D @types/node
npm run dev -- --open次のようなディレクトリが自動生成されます:
my-app/
├ src/
│  ├ lib/
│  ├ routes/
│  ├ app.html
├ static/
└ svelte.config.js3️⃣ Hello World:最速スターティング
src/routes/+page.svelte
<script lang="ts">
  let name = '世界';
</script>
<h1>こんにちは、{name}!</h1>
<input bind:value={name} placeholder="名前を入力" />解説:
- <script lang="ts">で TypeScript対応
- bind:valueによる双方向バインディング
- ファイル名 +page.svelteがルート/にマッピング
ブラウザでフォームに文字を打つと、即時にUIが更新されることを確認しましょう。
4️⃣ ファイルベースルーティング
ネストされたルート
src/routes/
├ +page.svelte      --> /
└ about/
   └ +page.svelte   --> /aboutアクセスすると、それぞれ違うコンポーネントが表示されます。
動的ルート
src/routes/blog/[slug]/+page.svelte
<script lang="ts">
  export let params: { slug: string };
</script>
<h1>記事: {params.slug}</h1>このように [] で囲むと動的URLを簡単実装できます。
5️⃣ SSR入門:load 関数
サーバー側でデータを取得し、初期HTML に埋め込む方法です。
src/routes/users/+page.ts
import type { PageLoad } from './$types';
export const load: PageLoad = async ({ fetch }) => {
  const res = await fetch('https://jsonplaceholder.typicode.com/users');
  const users = await res.json();
  return { users };
};src/routes/users/+page.svelte
<script lang="ts">
  export let data: { users: { id: number; name: string }[] };
</script>
<ul>
  {#each data.users as user}
    <li>{user.name}</li>
  {/each}
</ul>解説:
- loadでフェッチ → SSR
- ブラウザに届くHTMLに既にユーザー一覧がレンダリングされる
- SEO・初期表示速度 激的に向上
6️⃣ SPA化:クライアントサイドナビゲーション
ページ間移動が フルページリロードなし に。
標準リンク
<script>
  import { goto } from '$app/navigation';
</script>
<button on:click={() => goto('/about')}>Aboutへ</button>または
<a href="/about" sveltekit:prefetch>Aboutへ</a>解説:
- gotoはプログラム的に遷移
- sveltekit:prefetchでホバー時に事前取得
- ページ遷移が即時かつスムーズ
7️⃣ APIフェッチの使い分け
- SSR (load関数) → SEO重視・初期表示
- CSR (onMount) → インタラクティブ要素の更新
CSR例
<script lang="ts">
  import { onMount } from 'svelte';
  let post;
  onMount(async () => {
    const res = await fetch('/api/random');
    post = await res.json();
  });
</script>
{#if post}
  <article>{post.title}</article>
{:else}
  <p>読み込み中…</p>
{/if}8️⃣ デプロイ:Vercel / Netlify
Vercel
npm i -g vercel
vercel login
vercel --prodNetlify
GitHub連携で Automatic Deploy を設定。
9️⃣ 応用編:認証付きSSR/DB接続
+page.server.ts
import { redirect } from '@sveltejs/kit';
export const load = async ({ locals }) => {
  if (!locals.user) throw redirect(302, '/login');
  const todos = await db.getTodos(locals.user.id);
  return { todos };
};解説:
- localsで セッション情報 を SSR 時に受け取る
- 未ログイン ユーザーをクライアント前に リダイレクト
🔚 まとめ & 次のステップ
- SvelteKit は SSR×SPA を両立 する次世代フレームワーク
- load, goto, prefetch など独自 API が強力
- デプロイも数コマンドで完了 ⇒ 開発〜運用 の効率化
次は Edge Runtime や Prerendering、GraphQL 接続などを解説予定です!
おまけ:リアルタイム Todo アプリ構築
SvelteKit の SSR × SPA 機能 を活かして、リアルタイム同期付きの Todo アプリを作成します。
- SSR で初期データを配信
- SPA でクライアント側操作を即時反映
- WebSocket (Socket.IO) で多人数同時編集対応
🏗️ 構成概要
| 層 | 技術 | 
|---|---|
| フロントエンド | SvelteKit (+ TypeScript) | 
| API 層 | SvelteKit Endpoints (src/routes/api) | 
| リアルタイム同期 | Socket.IO (server + client) | 
| データベース | SQLite + Prisma ORM | 
データベース設定 (Prisma)
npm install prisma @prisma/client
npx prisma init --datasource-provider sqliteprisma/schema.prisma:
datasource db { provider = "sqlite" url = "file:./dev.db" }
generator client { provider = "prisma-client-js" }
model Todo {
  id        Int      @id @default(autoincrement())
  text      String
  done      Boolean  @default(false)
  createdAt DateTime @default(now())
}npx prisma migrate dev --name initAPI エンドポイント (CRUD)
src/routes/api/todos/+server.ts
import { json } from '@sveltejs/kit';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
export async function GET() {
  const todos = await prisma.todo.findMany({ orderBy: { createdAt: 'desc' } });
  return json({ todos });
}
export async function POST({ request }) {
  const { text } = await request.json();
  const todo = await prisma.todo.create({ data: { text } });
  return json({ todo });
}
export async function PUT({ request }) {
  const { id, done } = await request.json();
  const todo = await prisma.todo.update({ where: { id }, data: { done } });
  return json({ todo });
}
export async function DELETE({ request }) {
  const { id } = await request.json();
  await prisma.todo.delete({ where: { id } });
  return json({ success: true });
}解説:
- +server.tsファイルベースで REST API を実装
- GETで初回 SSR 配信用データ取得
- POST/- PUT/- DELETEでデータ操作後クライアントに JSON を返却
Socket.IO サーバー統合
src/hooks.server.ts
import { sequence } from '@sveltejs/kit/hooks';
import { Server } from 'socket.io';
import type { Handle } from '@sveltejs/kit';
let io: Server;
export const handle: Handle = sequence(async ({ event, resolve }) => {
  if (!io) {
    const server = await event.platform?.node?.httpServer; // Vercel 以外
    io = new Server(server);
    io.on('connection', socket => {
      socket.on('todo:update', data => {
        socket.broadcast.emit('todo:update', data);
      });
    });
  }
  return resolve(event);
});解説:
- hooks.server.tsで初回リクエスト時に Socket.IO サーバーを起動
- todo:updateイベントをクライアント間でブロードキャスト
フロントエンド実装
src/routes/+page.svelte
<script lang="ts">
  import { onMount } from 'svelte';
  import io from 'socket.io-client';
  let todos = [];
  let text = '';
  const socket = io();
  async function fetchTodos() {
    const res = await fetch('/api/todos');
    todos = (await res.json()).todos;
  }
  async function addTodo() {
    const res = await fetch('/api/todos', { method:'POST', body:JSON.stringify({ text }) });
    const { todo } = await res.json();
    todos = [todo, ...todos];
    socket.emit('todo:update', { action: 'add', todo });
    text = '';
  }
  function toggleDone(id) {
    const todo = todos.find(t => t.id === id);
    fetch('/api/todos', { method:'PUT', body:JSON.stringify({ id, done: !todo.done }) });
    todos = todos.map(t => t.id === id ? { ...t, done: !t.done } : t);
    socket.emit('todo:update', { action: 'toggle', id });
  }
  onMount(() => {
    fetchTodos();
    socket.on('todo:update', data => {
      if (data.action === 'add') todos = [data.todo, ...todos];
      if (data.action === 'toggle') todos = todos.map(t => t.id === data.id ? { ...t, done: !t.done } : t);
    });
  });
</script>
<input bind:value={text} placeholder="新しいTODOを入力" />
<button on:click={addTodo}>追加</button>
<ul>
  {#each todos as { id, text, done }}
    <li on:click={() => toggleDone(id)} class:done={done}>{text}</li>
  {/each}
</ul>
<style>
  .done { text-decoration: line-through; color: #888; }
</style>解説:
- SSR で取得した todosをonMountで初期化し、画面に描画
- CRUD API コール後に Socket.IO で他クライアントへ変更通知
- リアクティブ変数 todosの更新で即時 UI 反映

 
	 
	
 
	 
	 
	
 
	
 
	
 
	 
	 
 
	