はじめに
Svelteに興味があるけれど、公式チュートリアルを一通り読むのは大変…と感じる方もいるかもしれません。
本記事では、Svelteの基本的な概念から応用的な使い方までを、実践的なサンプルコードとともに要約しました。
ショートハンド記法・リアクティブな変数・ストア・非同期処理・イベント管理・CSSバインディングなど、Svelteの主要な機能をできるだけ分かりやすくまとめています。
チュートリアルの補助的な資料として、学習の手助けになればと思っています。
これからSvelteを学ぶ方、すでに触れたことがある方の復習としても活用いただければ幸いです。
※当記事ではSvelte単体の解説記事になります。SvelteKitの内容は含まれてません
この記事で紹介する内容
機能 | 概要 |
---|---|
ショートハンド記法 | プロパティを簡潔に記述 |
動的HTML (@html ) |
文字列として渡されたHTMLをレンダリング |
$derived |
既存のデータを元に派生した値を計算 |
$inspect |
データの変更をリアルタイムで確認 |
keyed each | 配列の要素削除時に連動させる |
非同期処理 (await ブロック) |
非同期データの取得と表示 |
イベントの親子順 (capture ) |
親が先にイベントを処理する |
bind を活用したデータバインディング |
フォーム入力などの要素とデータを連携 |
CSSクラスの適用 | クラスを条件付きで適用する方法 |
get / set | クラスのカウンター管理 |
store | 状態をコンポーネント間で共有 |
@snippet |
再利用可能なスニペットを定義 |
svelte:boundary |
エラーハンドリングの実装 |
ショートハンド
Svelteでは、プロパティのショートハンド記法が使えます。
通常の記法と比較すると簡潔になります。
Playgroundはこちら
<script>
let name = "Svelte";
</script>
<!-- 通常の記法 -->
<Component name={name} />
<!-- ショートハンド記法 -->
<Component {name} />
<script>
export let name;
</script>
<p>Hello, {name}!</p>
動的HTML
@html
を使用すると、文字列として渡されたHTMLをレンダリングできます。
ユーザー入力を直接 @html に渡す場合、XSS(クロスサイトスクリプティング)の危険があるため、十分に検証してください。
<script>
let htmlContent = "<strong>Dynamic HTML</strong>";
</script>
<p>{@html htmlContent}</p>
$derived
を使った派生状態の管理
Svelteの $derived を活用すると、他の変数の状態を元に派生値を計算できます。
<script>
let numbers = $state([1, 2, 3, 4]);
let total = $derived(numbers.reduce((t, n) => t + n, 0));
// $derivedをつけないとtotalが10のまま
// let total = numbers.reduce((t, n) => t + n, 0);
function addNumber() {
numbers.push(numbers.length + 1);
}
</script>
<p>{numbers.join(' + ')} = {total}</p>
<button onclick={addNumber}>
Add a number
</button>
$inspect
で状態が変化する度にログを出す
開発時に変数の変化を監視するために $inspect
を使用できます。
また、本番環境等ではログが出ないようになります。
<script>
let count = $state(0);
// `$inspect` を使って count の変更をリアルタイムで監視
$inspect(count);
// 初回の「0」のみ出力される
console.log("console", count)
function increment() {
count += 1;
}
</script>
<p>カウント: {count}</p>
<button onclick={increment}>+1</button>
keyed each で削除時に連動させる
リストをレンダリングする際に、各要素に一意のキーを持たせることで削除時の動作を適切に制御できます。
該当チュートリアル
<script>
import Thing from './Thing.svelte';
let things = $state([
{ id: 1, name: 'apple' },
{ id: 2, name: 'banana' },
{ id: 3, name: 'carrot' },
{ id: 4, name: 'doughnut' },
{ id: 5, name: 'egg' }
]);
</script>
<button onclick={() => things.shift()}>
Remove first thing
</button>
<!-- {#each things as thing} にするとname の値が更新されるが、絵文字は更新されない -->
{#each things as thing (thing.id)}
<Thing name={thing.name} />
{/each}
<script>
const emojis = {
apple: '🍎',
banana: '🍌',
carrot: '🥕',
doughnut: '🍩',
egg: '🥚'
};
let { name } = $props();
const emoji = emojis[name];
</script>
<p>{emoji} = {name}</p>
非同期処理 (await ブロック)
await ブロックを使うと、非同期処理の状態に応じてUIを更新できます。
Playgroundはこちら
<script>
async function fetchData() {
return new Promise(resolve => {
setTimeout(() => resolve("Loaded Data"), 2000);
});
}
</script>
<!-- 2秒後に"Loaded Data"と表示される -->
{#await fetchData()}
<p>Loading...</p>
{:then data}
<p>{data}</p>
{:catch error}
<p>Error: {error}</p>
{/await}
capture でイベントの親子の順番を逆にする
通常、イベントは子要素から親要素へ伝播しますが、capture
を使うと親要素が先にイベントを処理できます。
Playgroundはこちら
<script>
// Parent → Childの順で発火する
function handleParentClick() {
alert("Parent clicked!");
}
function handleChildClick() {
alert("Child clicked!");
}
</script>
<div onclickcapture={handleParentClick}>
Parent
<button onclickcapture={handleChildClick}>Child</button>
</div>
bind を使用したデータバインディング
Svelteでは bind: を使うことで、フォームの入力値や要素のプロパティをリアクティブに連携できます。
Playgroundはこちら
<script>
let name = "";
</script>
<!-- <input type="text" value={name}> にすると入力内容がDOMに反映されない -->
<input type="text" bind:value={name}>
<p>入力値: {name}</p>
CSSのクラスの付け方
Svelteでは class に配列を渡し、オブジェクト記法を用いることで条件付きクラスを適用できます。
<script>
let isActive = false;
</script>
<button on:click={() => isActive = !isActive} class={["button", { "is-active": isActive }]}>
Toggle Class
</button>
<style>
.button {
padding: 10px 20px;
font-size: 16px;
border: none;
cursor: pointer;
background-color: gray;
color: white;
transition: background-color 0.3s;
}
.is-active {
background-color: blue;
color: white;
}
</style>
get / set
Svelteでは、クラスの プライベート変数 (#) を利用しつつ、get / set を活用することで、カウンターの状態管理を安全に行う ことができます。
Playgroundはこちら
<script>
class Counter {
#count = $state(0);
constructor(initialValue = 0) {
this.#count = initialValue;
}
// `get` でカウントを取得
get count() {
return this.#count;
}
// `set` でカウントを変更(0未満にはならないよう制御)
set count(value) {
if (value >= 0) {
this.#count = value;
} else {
alert("カウントは0未満にはできません!");
}
}
increment() {
this.count = this.#count + 1;
}
decrement() {
this.count = this.#count - 1;
}
}
let counter = new Counter();
</script>
<p>カウント: {counter.count}</p>
<button onclick={() => counter.increment()}>+1</button>
<button onclick={() => counter.decrement()}>-1</button>
store
Svelteの store を活用すると、コンポーネント間での状態管理 が簡単にできます。
ここでは writable を使ってカウンターを作成し、リアクティブな更新されるようにします。
Playgroundはこちら
<script>
import { writable } from 'svelte/store';
// カウントのストアを作成(初期値 0)
let count = writable(0);
// カウンターを増減させる関数
function increment() {
count.update(n => n + 1);
}
function decrement() {
count.update(n => (n > 0 ? n - 1 : 0)); // 0未満にならないよう制御
}
</script>
<p>カウント: {$count}</p>
<!-- ストアの値を変更 -->
<button onclick={increment}>+1</button>
<button onclick={decrement}>-1</button>
@snippet
Svelteの @snippet は、コンポーネントの一部を再利用しやすくするための機能です。
基本の@snippet
の使い方
<script>
let message = "Hello, Svelte!";
</script>
<!-- Hello, Svelte! -->
{@render greetingSnippet()}
{#snippet greetingSnippet()}
<p>{message}</p>
{/snippet}
@snippet
にプロパティを渡す
特定の text を表示するボタンを @snippet にし、異なるボタンを作成できます。
<script>
let message = "ボタンをクリックしてください!";
function changeMessage(newMessage) {
message = newMessage;
}
</script>
<p>{message}</p>
<!-- スニペットのレンダリング -->
{@render customButton("Hello", () => changeMessage("こんにちは!"))}
{@render customButton("Goodbye", () => changeMessage("さようなら!"))}
{#snippet customButton(text, onClick)}
<button onclick={onClick}>{text}</button>
{/snippet}
@snippet
を使ったコンポーネントの分割
Svelteの@snippet
を使って、コンポーネント間で動的なスニペットを受け渡し、状態を共有できます。
Playgroundはこちら
<script>
import Children from "./Children.svelte";
let message = "ボタンをクリックしてください!";
function changeMessage(newMessage) {
message = newMessage;
}
</script>
<Children message={message} onChangeMessage={changeMessage}>
{@render customButton(message, changeMessage)}
</Children>
{#snippet customButton(message, changeMessage)}
<p>{message}</p>
<button onclick={() => changeMessage("こんにちは、Svelte!")}>挨拶する</button>
<button onclick={() => changeMessage("さようなら、Svelte!")}>別れる</button>
{/snippet}
<script>
let { children, message, onChangeMessage } = $props();
</script>
<div class="button-wrapper">
{@render children(message, onChangeMessage)}
</div>
<style>
.button-wrapper {
display: flex;
flex-direction: column;
gap: 10px;
}
</style>
svelte:boundary
を使ったエラーハンドリング
<script>
import FlakyComponent from './FlakyComponent.svelte';
</script>
<svelte:boundary onerror={(e) => console.error(e)}>
<FlakyComponent />
{#snippet failed(error, reset)}
<p>Oops! {error.message}</p>
<button onclick={reset}>Reset</button>
{/snippet}
</svelte:boundary>
<script>
let mouse = $state({ x: 0, y: 0 });
</script>
<svelte:window
onmousemove={(e) => {
mouse.x = e.clientX;
mouse.y = e.clientY;
}}
/>
<p>{mouse.x}x{mouse.y}</p>
<button onclick={() => mouse = null}>
押すとエラーが発生する
</button>
処理は下記の通りです
- ボタンを押すと mouse = null になり、エラーが発生 →
<svelte:boundary>
が処理される -
FlakyComponent.svelte
でエラーが発生すると、onerror でログを出力し、failed snippet を表示 -
reset()
を押すとエラーがリセットされる
おわりに
本記事では、Svelteの公式チュートリアルを要約しながら、基本の主要な機能を解説しました。
機能 | 概要 |
---|---|
ショートハンド記法 | プロパティを簡潔に記述 |
動的HTML (@html ) |
文字列として渡されたHTMLをレンダリング |
$derived |
既存のデータを元に派生した値を計算 |
$inspect |
データの変更をリアルタイムで確認 |
keyed each | 配列の要素削除時に連動させる |
非同期処理 (await ブロック) |
非同期データの取得と表示 |
イベントの親子順 (capture ) |
親が先にイベントを処理する |
bind を活用したデータバインディング |
フォーム入力などの要素とデータを連携 |
CSSクラスの適用 | クラスを条件付きで適用する方法 |
get / set | クラスのカウンター管理 |
store | 状態をコンポーネント間で共有 |
@snippet |
再利用可能なスニペットを定義 |
svelte:boundary |
エラーハンドリングの実装 |
本記事がSvelteの学習や復習の助けになれば幸いです!
最後までお読みいただき、ありがとうございました!