はじめに
2023/11/11に開催されたSvelte Summit Fall 2023でSvelte5のalpha版がリリースされました
この記事ではどういう機能が追加されたのか、どういう書き方に変わるかを紹介します。
現在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
<script>
let name = "John";
</script>
<h1>Hello {name}</h1>
Svelte5
<script>
let name = $state("John");
</script>
<h1>Hello {name}</h1>
$derived
$:
の代わりとして使用することができます。
従来、$:
はトップレベルでのみ使用可能であり、.svelte
ファイル内でしか機能しなかったため、リファクタリングが困難でした。これらの点が改善されたため、より使いやすくなりました。
Svelte4
<script>
let count = 10;
$: doubleCount = count * 2;
</script>
<div>{doubleCount}</div>
Svelte5
<script>
let count = $state(10);
const doubleCount = $derived(count * 2);
</script>
<div>{doubleCount}</div>
$effect
onMount
の代わりとして使用することができます。
特定の値が変更されたときや、コンポーネントがDOMにマウントされたときにコードを実行する必要がある場合に使用します。
Svelte4
<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
<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
<script>
import { beforeUpdate } from "svelte";
beforeUpdate(() => {
console.log("beforeUpdate");
});
</script>
Svelte5
<script>
$effect.pre(() => {
console.log("beforeUpdate");
});
</script>
$effect.active
Svelte5で追加された機能です。
コードがエフェクト内で実行されているのか、テンプレート内で実行されているのかを示す機能です。
<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は、コンポーネントの初期化フェーズの外側でエフェクトを作成することもできます。
<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
<script>
export let optionalProp = 42;
export let requiredProp;
</script>
<p>optionalProp:{optionalProp}</p>
<p>requiredProp:{requiredProp}</p>
Svelte5
<script>
let { optionalProp = 42, requiredProp } = $props();
</script>
<p>optionalProp:{optionalProp}</p>
<p>requiredProp:{requiredProp}</p>
$inspect
Svelte5で追加された機能です。
console.log
とほぼ同じですが、渡される引数が変更されると再び実行される、という特徴があります。$inspect
は、リアクティブなデータの変化を詳しく監視します。つまり、オブジェクトや配列の内部のデータにリアクティブな性質を持たせておき、そこでデータが更新された場合、その関数が再び実行されます。
<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
を使うことでコンポーネント内でマークアップを再利用することができます。
以下のような重複したコードを記述する代わりに...
{#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
を使ってこのように書けます。
{#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では他のプロパティと同じようにイベントハンドラを使用します。
<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で配信する予定になっていて気軽にご覧いただけますので是非参加してください。