本セッションの登壇者
セッション動画
では始めさせていただきます。今日はSvelteのこの1年の最新情報をキャッチアップできるということをゴールに発表します。
簡単に自己紹介ですが、山下と申します。株式会社フライルで働いていて、AIを使ったVOCの分析やプロダクトマネジメントワークの改善プラットフォームを運営しています。Svelteのコアチームメンバーです。
今日はまず「Svelteとは」を簡単におさらいした後に、最新情報ということで前の発表にもあったリアクティビティを実現する方法を共有した後、Slot/Snippetsやイベントハンドラー、その他ドキュメントにない情報や、ちょっとした面白情報みたいなことを共有できればと思います。
書くハードルが低い「HTMLのスーパーセット」
Svelteとは、フロントエンドを構築するためのフレームワークで、ReactやVue.jsなどの仲間です。「HTMLのスーパーセット」を謳っていて、基本的にはHTML、Java、CSSが書けるとSvelteも書けることが、Svelteの導入理由としてよく言われる点です。.svelte
ファイルを.jsファイルにコンパイルしてブラウザ上で動くという、コンパイラを使用している点が他のフレームワークと大きく異なります。また、他のフレームワークはランタイムですべての処理をするのに対して、Svelteはできる限りコンパイラでやるので、その分ランタイムの処理が軽量になります。
今リリースされている最新バージョンはSvelte 4で、Svelte 5が開発中です。チームの変更としては、今までのフルタイムメンバー2人に加えて、ドミニク・ガナウェイさんという元Reactコアチームメンバーで、lexicalとinfernoの作者の方が参加されました。TC39のメンバーにもなっていて、Signalsの提案の作成にも関わっています。
Svelte 5は、これまでの反省やユーザーからのフィードバックを踏まえて、まったくのゼロからコンパイラもランタイムもすべて書き直しました。その結果得られたものが何点かあります。
Runesマクロでリアクティビティを実現
1つ目がリアクティビティで、SvelteではRunesと呼んでいます。これが最も大きい変更の1つで、Svelte 4まではリアクティビティにダラーサイン($)を使っていたんですが、Svelte 5ではRunesと呼ばれるマクロを使います。
リアクティビティを実現するには、ステート管理、算出フィールド、エフェクトという大きくはこの3つの要素で構成されているわけですけれども、1つ目のステートに関しては、Svelte 4では let count = 0;
と書いていたのがSvelte 5では let count = $state(0);
と書きます。Svelte 4もSvelte 5も同じように動作します。
算出フィールドはSvelte 4では $: doubled = count * 2;
という感じで、ラベルで$を使うとリアクティビティを実現できたんですが、Svelte 5では $derived
を使います。
なぜこうしたかというと、ロジックというのは大体リファクタリングで共通化したくなるわけですけども、たとえば今の doubled
のロジックを getDoubled
のように共通化したいとします。Svelte4の場合は動きませんが、Svelte 5では動きます。
ここで、重要なSvelte 5での変更点があります。Svelte 4では依存関係の追跡をコンパイルタイムで実施していたので、推移的に呼び出される関数のステート書き換えに追従できませんでした。Svelte 5では、依存関係の追跡をランタイムで実施します。これによってcomposablesによるロジックの共有化がより柔軟になります。
エフェクト関数は $:{
で囲んでいたのが $effect()
で囲む書き方に変わっています。
次はオブジェクトです。今までSvelteでオブジェクトに対してリアクティブを発火するためには、再代入する必要がありました。Svelte 5ではpush関数などの破壊的関数の呼び出しでも機能します。
また、Svelteの場合、Svelte 4ではたとえばitems.pushという配列に何かをpushすると動きませんでした。たとえば、items = items;
のような謎の代入が必要です。Svelte 5では items.push
とするとちゃんとリアクティブになります。オブジェクトも同様です。
細かい話ですが、beforeUpdaate/fterUpdateはSvelte 6で廃止されます。なので、$effect.pre
や $efffect
というマクロを使ってください。
RunesのAPIの一覧はこのURLから見られます。
Slot/Snippetとイベントハンドラー
次はSlotです。ReactやVueとSvelteを比較した時にJSXの方が優れている理由の1つにファイル内で要素を簡単に使い回すことができる点がありました。Svelte 5ではこのJSXの利点を取り入れることに成功しました。それがSnippetsです。これは既存ユーザーにとっては非常に大きい進化だったと思います。デモは動画をご覧ください。
次はイベントハンドラーです。書き方が若干変わって、今までは on:click={
という書き方をしたんですが、onclick
という書き方に変わりました。この変更により、イベントはpropと同様に記述できるようになりました。また、今までイベントを宣言するためには、createEventDispatcherを使わなければならなくて面倒くさかったんですが、使う必要がなくなります。
たとえば、button
というHTML要素には、今までは on:click={
と書く必要がありましたが、{onclick}
と書くだけでクリックした時のイベントが発火されます。これは短くていいと思います。
また、スプレッド演算子で任意のイベントをコンポーネントが受け取れるようになりました。Svelte 4まではサードパーティーのライブラリを使うしかなかった機能です。
スピードも改善、パーサーは独自に
このあたりから面白い情報が増えてきます。Svelte 4までは各DOMにイベントがアタッチされていましたが、Svelte 5ではアプリケーションのルートにのみイベントをアタッチして、ランタイムによって具体的なイベントハンドラーを呼んでいきます。これは一般的にイベントデリゲーションと呼ばれている、React 17やSolidでも用いられている手法で、スピードやメモリフットポイントにいい影響が出るはずです。
次に、テンプレート部でTypeScriptが使えるようになりました。acorn-typescriptというライブラリを使っています。
Svelteのいいポイントとしてよく言われる ‘@const’ タグを使って、user.nameを宣言すると、エラーなく実行できます。
CSSパーサーについてですが、Svelte 4ではcss-treeというパーサーを使ってCSS部分を解析していたんですが、Svelte 5では独自のコードで解析するようになっています。Svelteにとっては簡易なパーサーで十分だということで、独自のパーサーを作ることにしました。これによって将来のCSSの新仕様に簡単に対応できます。なぜなら、CSS仕様の一部だけをパースして、残りの部分は見ないからですね。あと、css-treeもコンテナクエリをサポートしないので、対処するためにもSvelte 5では独自のパーサーを作っています。さらに、このメリットを生かしてコンポーネントにスタイルを渡す方法を検討しています。今までは ‘:global’ などを使う必要がありましたが、公式の機能として提供される可能性があります。
あと、これも面白い情報ですが、型定義ファイルに関して、Svelte 5ではdts-buddyというライブラリを使って型を自動生成しています。これによって今まで型情報が1.1 MBあったんですが、25 KBになりました。
また、関数から宣言に飛ぶ際、たとえば今までTypeScriptでこのonMountという関数に飛んだ時は型情報のみで実際の関数の中身が見られませんでした。でもSvelte 5ではonMountから、実際の関数を定義する.jsファイルに飛ぶことができます。これはdts-buddyによって実現されています。
Svelte 4からの移行に関しては、1コンポーネントずつ漸進的に移行可能なので、おもむろにライブラリだけSvelte 5に上げてもほぼ正しく動くはずです。今まで説明したSvelte 4からSvelte 5の違いはたくさんありますが、普通に使うことができます。
ベンチマークについては省略します。
SvelteはSvelteKitとの同時使用を推奨
まとめはこちらです。
Svelte 4とSvelte 5はComponent partyというサイトに書き方の比較表がまとまっているので、見ると分かりやすいと思います。
ReactでいうところのNext.jsやVue.jsのNuxt.jsにあたるSveltKitも、昨年末にメジャーバージョンアップをしてバージョン2になりました。Svelteチームとしては、基本的にSvelteをSvelteKitと一緒に使うことを推奨しているので、こちらも是非ドキュメントを見てもらえると嬉しいです。日本語版もあります。あとSvelte用のプラグインも提供していて、皆さんの近くの人たちが頑張って開発してくれているので、是非こちらもご活用いただければと思います。これで終わります。ありがとうございました。