--- title: "Svelte/SvelteKit の地味に好きなところ" date: '2024-09-06' isPublished: true lang: 'ja' --- 普段から `Svelte` / `SvelteKit` を使っているので、その地味に好きなところを紹介します。 また、微妙なところも最後に紹介します。 > [!NOTE] > 本記事では `Svelte5` を前提としています。 > 執筆時点では `Svelte5` はまだ RC版です。 # Who are you? - [ryoppippi](https://github.com/ryoppippi) - `Frontend` は Svelte メインで書いている - `React` も書くが、少なめ - `Astro` は書いている - `Solid` でも簡単なものを作ったことがある - `Vue3` はほぼ触ったことがない # Svelte の好きなところ👍 ## 直感的に書けるところ `Svelte` は直感的に書けるところが好き。 見た目も `html` に近い。 エディタの支援もしっかりしているので、書いていてストレスがない。 ## 速い、軽い、うまい 結構適当に作っても、speedInsight でいい点数を取りやすい。 また、ビルド後のファイルサイズも小さいので、デプロイもしやすい(特に `Cloudflare Pages` との相性がいい)。 ## 型安全がしっかりしているところ `tsx` と比較して型安全が疎かであるように思われているが、`Svelte` は型安全がしっかりしている。 ```html {#if fruit === 'apple'}

🍎

{:else if fruit === 'orange'}

🍊

{:else if fruit === 'banana'}

🍌

{:else if fruit === 'grae'}

🍇

{:else}

Unknown fruit {fruit as unknown as string}

{/if} ``` このように、ちゃんと型安全が効いている。 `Svelte5` からは template 部分でも TypeScript の記法が使えるようになったため、より柔軟性の高い記述が可能になった。 もちろん要素の属性に渡す値や関数も型安全である。 また、後述する `SvelteKit` ではより面白い型安全機能が追加されている。 ## `class:name`/ `style:name` が便利なところ > [!NOTE] > まず、`class` が `className` でない所。短く書けるので楽...。 `Svelte` では、通常の `class='foo'` に加えて、`class:foo` という書き方ができる^[https://svelte.jp/docs/element-directives#class-name]。 加えて、`class:foo={true}` という書き方で条件を指定することで、クラスの付与を制御できる。 ```html

``` これは `tailwindcss` や `UnoCSS` などの utility-first CSS フレームワークとの相性がいい。 ```html

``` `tsx` で似たようなことをやろうとすると、[`clsx`](https://github.com/lukeed/clsx#readme) 等の追加のライブラリが必要になり、そのぶん runtime cost が増える。 標準機能でこれができるのは、とても便利。 おまけに可読性が高い。 ちなみに、最近 `Astro` にも `class:list` が追加されて、`clsx` が不要になった^[https://docs.astro.build/en/reference/directives-reference/#classlist]。 `style` も `style:name={foo}` のように書くことができる^[https://svelte.jp/docs/element-directives#style-property]。 ```html

``` この書き方は、特に css variables を使うときに便利である。 ```html

``` こんな感じで、外部変数を既存の css に適用することができる。 ## `style` タグが使いやすいところ `Svelte` では、`style` タグ内で書いたスタイルは、そのコンポーネント内でのみ有効である。 いわゆる `scoped` スタイルである。 ```html ``` もちろん、`global` なスタイルも書ける。 ```html ``` また、未使用のスタイルがあれば警告を出してくれるし、ビルド時に未使用のスタイルは自動的に削除される。 とても便利。 ## `each` blocks に `:else` が使えること 本当に地味なポイントだが、`each` blocks に `:else` が使えて嬉しい。 ```html {#each items as item (item.id)}

{item}

{:else}

No items

{/each} ``` `for-else` 構文ってどんな言語にあるんだろう。 Python とか Zig とか? ## Rune が使いやすいところ `Svelte5` で導入される `rune` が使いやすい。 `rune` についてここで語ると長くなるので、詳しくは色々記事をご覧ください。 ただ、正式リリースまではAPIが安定しないのでご了承ください。 日本語資料: https://baseballyama.github.io/techfeed-20240424-svelte5/1 https://docs.google.com/presentation/d/14wc-XSfmXxfHu-2YMM8gJHANzZbAdkS8YSwS-rvf48U/edit https://zenn.dev/tomoam/scraps/375fb71c09fe0f 英語資料(このdemoが本当にわかりやすいので、字幕をつけて見てほしい): https://www.youtube.com/watch?v=_SpO5T96AYY https://svelte-5-preview.vercel.app/docs/runes 自分は昨年発表されてからずっと `rune` を使っているが、かなり使いやすい。 正直 `Svelte4` 以前には戻れない。それくらい書き味が良い。 見た目は `Vue` の `Ref` であり、省略記法な `React` の `useState` であるが、コンパイラを経由することで書き心地とパフォーマンスを両立している。 他にも `snippet` 構文が最高だったり、`$props()` を用いた型安全な props の宣言が可能になったりと、 `Svelte5` はとても良いものだが、いかんせんリリースが遅れに遅れているのが残念。 年度末の Advent Calender では思う存分 `rune` についての記事を書きたい。 ```html {#snippet countDiv(count: number)}
Count: {count}
{/snippet} {@render countDiv(count)} ``` # SvelteKit の好きなところ👍 ## Zero-effort type safety https://svelte.jp/blog/zero-config-type-safety `SvelteKit` では `+page.server.ts` と `+page.svelte` というファイルを用いて、サーバーの処理とクライアントの処理を分離することができる。 `Remix` でいうところの `loader` と `component` に相当するものである(厳密に言えば `+page.server.ts` には `actions` も書ける)。 > [!NOTE] > `+page.server.ts` では `load` 関数をエクスポートすることで、サーバーサイドでのデータ取得を行う。 > `+page.svelte` では `data` という変数をエクスポートすることで、クライアントサイドでのデータ取得を行う。 > クライアントサイドでの処理は `+page.svelte` 内の `script` タグ内で行う(`Vue3` の `script setup` に相当)。 すごいのは、この2つのファイルにまたがっているデータのやりとりが自動的に型安全になることである。 `typeof load` みたいなことをする必要がなく、`+page.server.ts` の `load` 関数の戻り値を変更すると即座に `+page.svelte` の `data` の型も変わる。 ![form_3](./form_3.gif) _`+page.server.ts` の戻り値を変更すると、`+page.svelte` の `data` の型も自動的に変わる_ また、path の params なども自動的に型安全にしてくれる^[https://kit.svelte.jp/docs/routing#page-page-server-js]。 自分で、型を指定する必要はない。 ```ts // src/routes/blog/[slug]/+page.server.ts export async function load({ params }) { // slug は string であることが型安全に保証される ↓ const post = await getData(params.slug); if (post) { return post; } return error(404, 'Not found'); }; ``` コードを書けば自動で型がついてくれる体験は病みつきになる。 ## Web 標準技術を駆使しているところ `SvelteKit` は、`Svlte` の文法こそ独自であるものの、ベースとなる技術は Web 標準技術を駆使している。 https://kit.svelte.jp/docs/web-standards 例えば、`fetch`関数、`Request` クラス、`Stream APIs`、`Form Actions` などなど。 ここ1年ほど `React` 界隈でやっと 'Form Actions' やら `Progressive Enhancement` が話題になってきたが、`SvelteKit` はそれに先駆けて Web 標準を謳っていた印象がある。 これについても過去に記事を書いたので、興味があれば読んでみてください。 /blog/2023-04-28-zenn-aea8dcbc21c39e-ja ## Server / Client がファイルベースで分離されているところ 先ほども述べたが、`SvelteKit` では `+page.server.ts` と `+page.svelte` というファイルを用いて、サーバーの処理とクライアントの処理を分離することができる。 また、`.server` がついているファイルはクライアント側で読み込むことができない。 環境変数も、`PUBLIC_` がついているものだけがクライアント側で読み込むことができる。 クライアント側に不要な情報が漏れることを防ぐ仕組みができていることは、開発する上で安心感を与えてくれる。 /blog/2023-04-26-zenn-8addfe62eb4d3e-ja # Svelte / SvelteKit の微妙なところ🤔 色々あるが... ## 対応しているライブラリが(比較的)少ない `React` に比べると、対応しているライブラリが少ない。 特に UI 周り。 [shadcn-svelte](https://www.shadcn-svelte.com/) や [melt-ui](https://melt-ui.com/) など使い勝手のいいものはそこそこあるが、まだまだ少ない。 `React Aria` の `Svelte` 版があればいいのに...。 また、[`vueUse`](https://vueuse.org/) に匹敵するような便利関数詰め合わせライブラリもまだまだ発展中。 そもそも `Svelte5` でこれまでの `store` から `rune` への移行の過渡期であるため、安定してこないところもある。 とはいえ、ある程度素の `JavaScript` ライブラリが使えるので、そこまで困ることはない。 ## route が型安全にならない (SvelteKit) `fetch` 関数を使う時や、`a` タグでリンクを貼るときの文字列が型安全にならない。 一応、存在しないpathを指定すると、**ビルド時にエラーが出るので** 気づくことはできるが、開発中に型安全であると嬉しいなと思う([`Next.js`のtypedRoutes ](https://nextjs.org/docs/app/api-reference/next-config-js/typedRoutes)みたいなもの希望)。 現状では[`vite-plugin-kit-routes`](https://www.kitql.dev/docs/tools/06_vite-plugin-kit-routes) というプラグインで route を型安全にすることができるが、これが標準であると嬉しい。 https://www.kitql.dev/docs/tools/06_vite-plugin-kit-routes ## Server Endpoint の Response が型安全にならない (SvelteKit) Server endpoint の結果も型安全になっていない(普通に `fetch` 関数で叩いてデータを取得しているだけなので)。 現状、Server Endpoint の型安全を保証したいなら、`trpc` や `Hono RPC` と組み合わせる必要がある。 上で述べた通り、`SvelteKit` は色々と型安全を頑張っているので、もう一踏ん張りしてほしい。 (そもそも Server Endpoint を使う必要はあるのか、全部 `+page.server.ts` の `load` 関数内に書いてしまえばいいのでは? という意見もある) > [!NOTE] > ちなみに `Form` の型安全については、[superfoms](https://superforms.rocks/) というライブラリがある。 ## `tsx` じゃない 個人的には `tsx` があまり好きになれないのだが、これは好みの問題ではない。 `ts`/`tsx` でないことの最大のデメリットは、マクロ系のライブラリが使えないこと。 例えば [`unplugin`](https://github.com/unplugin) という、ビルド時に便利な処理をしてくれるプラグインたちがいるのだが、彼らは大抵 `ts`/`tsx` の処理に特化している。 そのため、`vue` / `svelte` / `astro` などの非 `ts`/`tsx` なフレームワークの対応が遅れがちになる。 (個人的には [`unplugin-macros`](https://github.com/unplugin/unplugin-macros)) が使えないのがたまに辛いこともある) とはいえ、必要な処理を `ts` ファイルに切り出せばいいので、そこまで困ることはない。データの処理であれば `+page.server.ts` に逃すこともできるので。 まあ、本当に必要ならば自作するのですが...。 # まとめ ざっくり `Svelte` / `SvelteKit` の好きなとこを紹介した。