Svelte v5 Runesによる$stateと$effectの使い方

2024年10月22日に最新のSvelte v5のstable版がリリースされました。
最新のSvelte 5は多くの新機能が追加され、その中、Runes（ルーン）が導入されることは最大の特徴です。
Svelte v5では、これまでの`$:`等のリアクティブな記法を廃止し、新たにRunes APIを導入しました。
Runes（ルーン）とは、`.svelte`や`.svelte.js` / `.svelte.ts`ファイル内で使用される記号で、Svelte コンパイラを制御するためのものです。Svelteを一つの言語と考えた場合、ルーンはその構文の一部であり、キーワードに相当します。

## Runes $stateと$effect
Runesでは、`$state`、`$effect`、`$derived`、`$props`、`$bindable`、`$inspect`、`$host`などの関数を提供します。
後述でsvelteの初期構築を始め、React、Vueの記法と比較しながら、`$state`と`$effect`を簡単に紹介します。

## Svelteプロジェクトの新規作成
Viteを通してSvelteプロジェクトを簡単に作成することができます。以下のコマンドを実行するだけです。

```terminal:terminal

npx sv create svelte-try
```
![Xnapper-2025-03-07-23.04.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3952033/df0f03de-3e36-4743-bdda-0c6bca95bd88.png)

こんな流れで簡易的に作成できました。
- テンプレートを選択
  - SvelteKit minimal を選択
- TypeScript の使用を選択
  - Yes, using TypeScript syntax を選択
- 追加する機能を選択
  - none を選択
- パッケージマネージャーを選択
  - pnpm を使用


ディレクトリ構成一覧
![Xnapper-2025-03-07-23.08.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3952033/8940d8c7-6d03-400d-a16f-20e734365c69.png)

## Svelteのコンポーネント構造について
Vueの単一ファイルコンポーネントと非常によく似ており、同様にscript、template、style 3つのエリアに分かれています。
![Xnapper-2025-03-07-23.16.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3952033/6457fe16-1420-4496-9cf0-f19bfad90fbc.png)

## リアクティビティ

`$state`はリアクティブな状態を作成するためのもので、変更があるとUIが自動的に反映します。
これはReactの`useState`やVueの`ref`に相当します。

```react:React
import { useState } from "react"

export const Test = () => {
  const [ count ] = useState(0);
  const [ name ] = useState("Hello World");

  return (
    <div>
      <p>{count}</p>
      <p>{name}</p>
    </div>
  )
}
```

```vue:Vue
<script setup>
import { ref } from "vue";

const count = ref(0);
const name = ref("Hello World");
</script>

<template>
  <div>
    <p>{{ count }}</p>
    <p>{{ name }}</p>
  </div>
</template>
```

```svelte:Svelte
<script>
  const count = $state(0);
  const name = $state("hello world");
</script>

<div>
  <p>{count}</p>
  <p>{name}</p>
</div>
```

## 副作用
`$effect`は$stateが変更された時に自動的に実行される処理で、リアクティブな副作用を管理します。
これはReactの`useEffect`やVueの`watch`に相当します。

```react:React
import { useState, useEffect } from "react";

export const Test = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log("count が変更されました:", count);
  }, [count]);

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  );
};
```

```vue:Vue
<script setup>
import { ref, watch } from "vue";

const count = ref(0);

watch(count, (newValue) => {
  console.log("count が変更されました:", newValue);
});
</script>

<template>
  <div>
    <p>{{ count }}</p>
    <button @click="count++">+</button>
  </div>
</template>
```

```svelte:Svelte
<script>
  const count = $state(0);

  $effect(() => {
    console.log("count が変更されました:", count);
  });
</script>

<div>
  <p>{count}</p>
  <button on:click={() => count++}>+</button>
</div>
```

上記のコードでは、Svelteが引き続きeffectを使用して、リアクティブデータの変更を監視していることがわかります。また、ReactのsetStateのような操作を使わずに、直接的にデータを変更することができ、Vueのように.valueを使用する必要もないです。
Svelteの方はもっと簡潔に分かりやすく見える感じです。


## 最後に
React、VueからSvelteへ移行する際に技術的なコストがほとんどかからず、さらに仮想DOMを使用しない高性能な特性やVueに近いより完全なリアクティブなデータ駆動を体験できるということです。

次回は`$derived`、`$props`について紹介したいと思います。

### 参考にさせていただいたサイト

https://svelte.dev/docs/svelte/v5-migration-guide