Svelte5のalpha版がリリースされたので新機能を紹介します！

# はじめに
2023/11/11に開催された[Svelte Summit Fall 2023](https://www.youtube.com/watch?v=pTgIx-ucMsY)でSvelte5のalpha版がリリースされました:tada:
この記事ではどういう機能が追加されたのか、どういう書き方に変わるかを紹介します。
現在Svelte5に向けて精力的に開発が進められていて日々新機能が追加されてます。
この記事はすぐに情報が古くなったりする可能性がありますので注意してください。
記事執筆時点のバージョンは`svelte@5.0.0-next.16`です。

## Runes
> Runes は Svelte コンパイラに働きかけるシンボルです。現在の Svelte では `let`、`=`、`export`キーワード、そして `$:` ラベル を特殊なものとして意味するように使用するのに対し、Rune は同様のことを 関数の構文(function syntax) で実現します。

引用：https://svelte.jp/blog/runes#what-are-runes

### $state
リアクティブなステートを宣言するには、`$state`を使用します。
書くことが増えて複雑になったと思うかもしれないですが、アプリケーションが複雑になってくるにつれ、どの値がリアクティブでどの値がリアクティブでないのか判別するのが難しくなってきますし、コードの振る舞いが`.svelte`と`.js`で変わってくるとリファクタリングが難しくなる等の理由でこういった変更がされたようです。

#### Svelte4

[デモ](https://svelte.dev/repl/88499da26f7d4ebfa5a29b2498b885f7?version=4.2.8)

```svelte
<script>
  let name = "John";
</script>

<h1>Hello {name}</h1>
```

#### Svelte5

[デモ](https://svelte-5-preview.vercel.app/#H4sIAAAAAAAAEz2MQQrCMBBFrzIMLhSE4jbGgjvxCqaL0k5pIE1CZiqUkLubbFy-x38_42IdMapPRj9uhAqfMeIV5YgN-EtOqDKHPU3NaJ6SjdIbD-BIoFXwgBPLKHQ2-A6rN3i5G6-7_7TCeutf5FyA3Iqiuyrq7xZmu1iaUUnaqQzlB54nY0GSAAAA)

```svelte
<script>
  let name = $state("John");
</script>

<h1>Hello {name}</h1>
```

### $derived
`$:`の代わりとして使用することができます。
従来、`$:` はトップレベルでのみ使用可能であり、`.svelte`ファイル内でしか機能しなかったため、リファクタリングが困難でした。これらの点が改善されたため、より使いやすくなりました。

[デモ](https://svelte.dev/repl/71fbbd741f94429488a6000dd75652f1?version=4.2.8)

#### Svelte4
```svelte
<script>
  let count = 10;
  $: doubleCount = count * 2;
</script>

<div>{doubleCount}</div>
```

[デモ](https://svelte-5-preview.vercel.app/#H4sIAAAAAAAAE02OTQrCMBCFrzIMLqoIVZexForHUBc1mUIgTUIyCUjJ3W0QxeX3_ngLTtpQRHFb0I4zocDBe9wjv3yFmMkwrRxdCrIqXZRBe-7vFsAQg3TJMlxgE3lkao6H7bla0tnIoFx6Grp-I4qCzqSaT2cHp5rt2t_iCkrnfvmrla6t0vpgdkpPmhQKDonKo7wB3SLfbbwAAAA=)

#### Svelte5
```svelte
<script>
  let count = $state(10);
  const doubleCount = $derived(count * 2);
</script>

<div>{doubleCount}</div>
```

### $effect
`onMount`の代わりとして使用することができます。
特定の値が変更されたときや、コンポーネントがDOMにマウントされたときにコードを実行する必要がある場合に使用します。

#### Svelte4

[デモ](https://svelte.dev/repl/b0943c5204b64fa38a521c7c786a7fde?version=4.2.8)

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

  let pageTitle = "";

  onMount(() => {
    pageTitle = document.title;
  });
</script>

<svelte:head>
	<title>Svelte4</title>
</svelte:head>

<p>Page title is: {pageTitle}</p>
```

#### Svelte5

[デモ](https://svelte-5-preview.vercel.app/#H4sIAAAAAAAAE1WPQQqDMBBFrzIMLhRKXXUTY6A3KLS72kWIYxvQGMxYKOLdaxQEl__xPn9mwsa2FFA8J3S6IxR49R5PyD8fQ_hSy7Tk0I-DiUQGM1jPqnIALTF4_aaH5ZaghCSwZkorrDArKheVhJqGDKdpBqWCKSI4dOrejB05PnMERRTmWJb5PrSE7Q7xIV1HwHK11X3FF5lvcW0dTSe9ui1zsBpgg4Bpn59l7tXyXNfXtrFUo-BhpPk1_wFVI9YzFwEAAA==)

```svelte
<script>
  let pageTitle = $state("");

  $effect(() => {
    pageTitle = document.title;
  });
</script>

<svelte:head>
	<title>Svelte5</title>
</svelte:head>

<p>Page title is: {pageTitle}</p>
```

### $effect.pre
`beforeUpdate`の代わりとして使用することができます。
DOMが更新される前にコードを実行する必要がある場合に使用します。

#### Svelte4

[デモ](https://svelte.dev/repl/05cf8a51dc7b4c5b8cbeef0c0d1f59d4?version=4.2.8)

```svelte
<script>
  import { beforeUpdate } from "svelte";
	
  beforeUpdate(() => {
  	console.log("beforeUpdate");
  });
</script>
```

#### Svelte5

[デモ](https://svelte-5-preview.vercel.app/#H4sIAAAAAAAAEy2NQQrCMBRErxI-LlqQuq-14CFcNS5qOpFA2h-SX0FC7m4Drob3GGYyWeeRqJ8ybfMK6ukeAp1JvqFC-sALDk68R1PNkEx0QUa9KXWCtTDShYimadVtVLlqLYa3xB6d53ej6QXLEY-wzAJN7bV2So3h8h87DlZenHVYqJe4ozzLD8k3YAubAAAA)

```svelte
<script>
  $effect.pre(() => {
  	console.log("beforeUpdate");
  });
</script>
```

### $effect.active
Svelte5で追加された機能です。
コードがエフェクト内で実行されているのか、テンプレート内で実行されているのかを示す機能です。

[デモ](https://svelte-5-preview.vercel.app/#H4sIAAAAAAAAE3WPQQrCMBBFrzIGoS3Ydh9rwHNYFyVOJJAmQzMVpPTuJiouFJfv8_9jZhHGOoxCnhbhhxGFFEcisRN8pwzxho4xcQzzpHPSRT1ZYtV7AB18DA4bF65lYX3ikYJHzxCRZ5LFDrZoDGpuBs32hmVV7aFtwQwuYu-z410oywoOCpYU8o_3Vfmv42nG7Fqrfe-79nNhAlJpzziSGxglLN-CtWtJQbep66cF6lqlb8dwscbiRcgcruf1AeLrf9woAQAA)

```svelte
<script>
  console.log('in component setup:', $effect.active()); // false

  $effect(() => {
	 console.log('in effect:', $effect.active()); // true
  });
</script>

<p>in template: {$effect.active()}</p> <!-- true -->
```

### $effect.root
Svelte5で追加された機能です。
自動クリーンアップを行わない非追跡スコープを作成する機能です。これは、手動で制御したいネストされたエフェクトに便利です。このRuneは、コンポーネントの初期化フェーズの外側でエフェクトを作成することもできます。

[デモ](https://svelte-5-preview.vercel.app/#H4sIAAAAAAAAE32PwQrDIAyGX0VkUAuj27lrB3uOuUOxcQhWReNglL771LaDXXYKyWe-38xUKg2BtveZmmEC2tKbc_RI8e1yE16gEVIfbPQiT7ogvHJ45YajBiTCRoOkJ4eAAwI715dEEhPWhEQ1DCa6zEFKENh4a5GxmvRXMud3HDfyO1wFVkOj7ZOVkGJOYNkjOHrA6A35s1mtcpJj999UX1OpxdedtrvSrZMdlVQw0hZ9hOWxfAArCFyhJgEAAA==)

```svelte
<script>
	let count = $state(0);

	const cleanup = $effect.root(() => {
		$effect(() => {
			console.log(count);
		});

		return () => {
			console.log('effect root cleanup');
		};
	});
</script>
```

### $props
`export let`の代わりとして使用することができます。
通常のjsとは異なる振る舞いとして使用されていたので違和感を持たれる方もいたと思いますが、その点が改善されました。
また、`$$props`や`$$restProps`といった機能の代わりにもなります。

#### Svelte4

[デモ](https://svelte.dev/repl/148e75c3c6dc4d1a864d92ef0cdcb68e?version=4.2.8)

```svelte
<script>
	export let optionalProp = 42;
	export let requiredProp;
</script>

<p>optionalProp：{optionalProp}</p>
<p>requiredProp：{requiredProp}</p>
```

#### Svelte5

[デモ](https://svelte-5-preview.vercel.app/#H4sIAAAAAAAAE02OQQrCMBBFrxIGFwqFgriqseANurcuSjuFQJqMyVSQkIt4Fu_kFUy6Mbt5n_c_E2BWGj00twBmWBAauBJBBfyiDP6JmjGxt6sbcyL96BRx25ueNbIIwhIrawbdOUviIk7HSjh8rMrhtEUxhTtKl98fzr2R9X_BSGrL-vfzDiVHWVO7WeVitkrerPTjYic1K5ygYbdivMcfKxkMwt4AAAA=)

```svelte
<script>
	let { optionalProp = 42, requiredProp } = $props();
</script>

<p>optionalProp：{optionalProp}</p>
<p>requiredProp：{requiredProp}</p>
```

### $inspect
Svelte5で追加された機能です。
`console.log`とほぼ同じですが、渡される引数が変更されると再び実行される、という特徴があります。`$inspect`は、リアクティブなデータの変化を詳しく監視します。つまり、オブジェクトや配列の内部のデータにリアクティブな性質を持たせておき、そこでデータが更新された場合、その関数が再び実行されます。

[デモ](https://svelte-5-preview.vercel.app/#H4sIAAAAAAAAE0WPzQ6CMBCEX6VpSIRIgmcsJB59BvUAZdXGdtvQLYlp-u6C-HOcnW92MpFflQbP61Pk2BngNT84x0tOT7cIP4EmmLW3YZTLRXg5KkftGc-kgZi0AYk1LPPUEeS7Yv91DHjf3eDvbe6gtd28iZnJFHoHkvK4Pil_ibQgovo3oegDkUVmUWolH03MC9a0a267Te0R5QgGkES1knNKKHSBWK9wqKdOB2jipyCxqp03GTuoq4KB1zQGSJf0AncTWtUOAQAA)

```svelte
<script>
	let count = $state(0);
	let message = $state('hello');

	$inspect({ count, message });
</script>

<button onclick={() => count++}>Increment</button>
<input bind:value={message} />
```

## Snippets
`slot`の代わりとして使用することができます。
`#snippet`、`@render`を使うことでコンポーネント内でマークアップを再利用することができます。

以下のような重複したコードを記述する代わりに...

[デモ](https://svelte-5-preview.vercel.app/#H4sIAAAAAAAAE5VUYW-kIBD9K8Tmsm2yXXRzvQ-s3eR-R-0HqqOQKhAZb9sz_vdDkV1t000vRmHewMx7w2AflbIGG7GnPlK8gYhFv42JthG-m9Gwf6BGcLbVXZuPSGrzVho8ZirDGpDIhldgySN5GpEMez9kaNuckY1ANJZRamRuu2ZnhEZt6a84pvs43mzD4pMsUDDi8DMkQFYCGdkvsJwblFq5uCik9bmJ4JZwUkv1eoknWigX2eGNN6aGXa6bjV8ybP-X7sM36T58SVcrIIV2xVIaA41xeD5kKqWXuqpUJEefOqVuOkL9DfBchGrzWfu0vb-RpTd3o-zBR045Ga3HfuE5BmJpKauuhbPtENlUF2sqR9jqpsPSxWsMrlngyj3VJiyYjJXb1-lMa7IWC-iSk2M5Zzh-SJjShe-siq5kpZRPs55BbSGU5YPyte4vVV_VfFXxVb10dSLf17pS2lM5HnpPxw4Zpv6x-F57p0jI3OKlVnhv5V9wPQrNYQQ9D_f6aGHlC89fq1Z3qmDkJCTCweOGF4VUFSPJvD_DhreVdA0eu8ehJJ5x91dBaBkpWm3ureCFPt3uzRv56d4kdp-2euG38XZ6dsnd3ZmPG9yRBCrzRUvi-MccOdwz3qE-fOZ7AwAhlrtTUx3c76vRhSwlFBHDtoPhefgHX3dM0PkEAAA=)

```svelte
{#each images as image}
	{#if image.href}
		<a href={image.href}>
			<figure>
				<img
					src={image.src}
					alt={image.caption}
					width={image.width}
					height={image.height}
				/>
				<figcaption>{image.caption}</figcaption>
			</figure>
		</a>
	{:else}
		<figure>
			<img
				src={image.src}
				alt={image.caption}
				width={image.width}
				height={image.height}
			/>
			<figcaption>{image.caption}</figcaption>
		</figure>
	{/if}
{/each}
```

`#snippet`、`@render`を使ってこのように書けます。

[デモ](https://svelte-5-preview.vercel.app/#H4sIAAAAAAAAE5VUYW-bMBD9KxbRlERKY4jWfSA02n5H6QcXDmwVbMs-lnaI_z6D7TTt1moTAnPvzvfenQ_GpBEd2CS_HxPJekjy5IfWyS7BFz0b9id0CM62ajDVjBS2MkLjqZQldoBE9KwFS-7I_YyUOPqlRGuqnKw5orY5pVpUduj3mitUln5LU3pI0_UuBp9FjTwnDr9AHETLMSeHK6xiGoWSLi9yYT034cwSRjohn17zcQPNFTs8s153sK9Uv_Yh0-5_5d7-o9zbD-UqCaRWrllSYZQxLw_HUhb0ta-y4NnJUxfUvc7QuLJSaO0a3oh2MLBZat8u-wsPnXzKQvTtVVF34xK5d69ThFmHEQ4SpzeVRediTG8rjD5vBSeN3E5JyHh6R1DQK9-iml5kjzQUN_lSgVU8DhYLx7wwjSvRkMDvTjiwF4zM1kXZ7DlF1eN3A7IG85e-zRrYEjjm0FkI4Cc7Ripm0pHOChexhcWXzreeZyRMU6Mk3ljxC9w4QH-cQZ_b3T5pjHxk1VNr1CDrnJy5QDh6XLO6FrLNSRb2l9gz0wo3S6m7HErSgLsPGMHkpDZK31jOanXeHPQz-eruLHUP0z6yTbpbrn223V70uMXNSpQSZjpL0y8hcxxpNqA6_ql3BQAxlxvfpQ_uT9GrWjQC6iRHM8D0MP0GQsIi92QEAAA=)

```svelte
{#snippet figure(image)}
	<figure>
		<img
			src={image.src}
			alt={image.caption}
			width={image.width}
			height={image.height}
		/>
		<figcaption>{image.caption}</figcaption>
	</figure>
{/snippet}

{#each images as image}
	{#if image.href}
		<a href={image.href}>
			{@render figure(image)}
		</a>
	{:else}
		{@render figure(image)}
	{/if}
{/each}
```

## Event handlers
Svelte4では`on:`ディレクティブを使用してイベントリスナーを要素にアタッチしていましたが、Svelte5では他のプロパティと同じようにイベントハンドラを使用します。

```diff_svelte
<script>
	let count = $state(0);
</script>
- <button on:click={() => count++}>
+ <button onclick={() => count++}>
	clicks: {count}
</button>
```

# さいごに
Svelte5どうでしょうか？ 
今までSvelteを書いてきた人は少し複雑になったと感じた人もいるかもしれません。
私は業務でSvelteを書いているので実践的なコードを書く経験があるんですが、実践的なコードを書くとどうしても複雑なことをやらないといけないことがあります。そういった場合に今回のSvelte5の変更によってコードが読みやすくなったと感じますし、よりJavaScriptの基本に近くなったり、使いにくかった機能がなくなったりして、学習コストは少なくなったのかなと個人的には思っています。
リリースが楽しみです！

# お知らせ
2023/12/8 19:00~ Svelte Japan Online Meetup #1が開催されます！
当日はYouTubeで配信する予定になっていて気軽にご覧いただけますので是非参加してください。

https://svelte-jp.connpass.com/event/297474/