@ryoppippi

Svelte/SvelteKit の地味に好きなところ

6 Sept 2024 ・ 14 min read


普段から​ Svelte / SvelteKit を​使っているので、​その​地味に​好きな​ところを​紹介します。 また、​微妙な​ところも​最後に​紹介します。

Note

本記事では​ Svelte5 を​前提と​しています。 執筆時点では​ Svelte5 は​まだ​ RC版です。

Who are you?

  • ryoppippi
  • Frontend は​ Svelte メインで​書いている
  • React も​書くが、​少なめ
  • Astro は​書いている
  • Solid でも​簡単な​ものを​作った​ことがある
  • Vue3 は​ほぼ​触った​ことがない

Svelte の​好きな​ところ👍

直感的に​書ける​ところ

Svelte は​直感的に​書ける​ところが​好き。 見た​目も​ html に​近い。 エディタの​支援も​しっかりしているので、​書いていて​ストレスが​ない。

速い、​軽い、​うまい

結構​適当に​作っても、​speedInsight で​いい​点数を​取りやすい。 また、​ビルド後の​ファイルサイズも​小さいので、​デプロイもしやすい​(特に​ Cloudflare Pages との​相性が​いい)。

型安全が​しっかりしている​ところ

tsx と​比較して​型安全が​疎かであるように​思われているが、Svelte は​型安全が​しっかりしている。

<script lang="ts">
	const fruit: 'apple' | 'orange' | 'banana' | 'grape' = 'apple';
</script>

{#if fruit === 'apple'}
<p>🍎</p>
{:else if fruit === 'orange'}
<p>🍊</p>
{:else if fruit === 'banana'}
<p>🍌</p>
{:else if fruit === 'grae'}
<!-- grape が typo しているのでエラーになる -->
<p>🍇</p>
{:else}
<!-- fruit は never なので cast が必要 -->
<p style="color: red">Unknown fruit {fruit as unknown as string}</p>
{/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} と​いう​書き方で​条件を​指定する​ことで、​クラスの​付与を​制御できる。

<p class="foo" class:bar class:baz="{true}" class:qux="{false}" />

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

<p
	class="text-white"
	class:text-red="{isDanger}"
	class:text-blue="{isInfo}"
	class:text-green="{isSuccess}"
	class:text-yellow="{isWarning}"
/>

tsx で​似たような​ことを​やろうと​すると、clsx 等の​追加の​ライブラリが​必要に​なり、​そのぶん 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

<p
  style='color: red'
  style:margin-top='{foo}rem'
  style:background-color={darkMode ? 'black' : 'white'}
/>

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

<script>
	const color = '#ff3e00';
</script>

<p style:--color="{color}" />

<style>
	p {
		color: var(--color);
	}
</style>

こんな​感じで、​外部​変数を​既存の​ css に​適用する​ことができる。

style タグが​使いやすい​ところ

Svelte では、style タグ内で​書いた​スタイルは、​その​コンポーネント内で​のみ​有効である。 いわゆる​ scoped スタイルである。

<style>
	p {
		color: red;
	}
</style>

もちろん、global な​スタイルも​書ける。

<style global>
	:global {
		body {
			background-color: black;
		}
	}
</style>

また、​未使用の​スタイルが​あれば​警告を​出してくれるし、​ビルド時に​未使用の​スタイルは​自動的に​削除される。 とても​便利。

each blocks に​ :else が​使える​こと

本当に​地味な​ポイントだが、each blocks に​ :else が​使えて​嬉しい。

{#each items as item (item.id)}
<p>{item}</p>
{:else}
<p>No items</p>
{/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 に​ついての​記事を​書きたい。

<script lang="ts">
	import type { ComponentProps } from 'svelte';
	type Props = { count: number } & ComponentProps<typeof HTMLDIVElement>;

	const { cont, ...rest }: Props = $props();
</script>

{#snippet countDiv(count: number)}
<div {...rest}>Count: {count}</div>
{/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 +page.server.ts の​戻り値を​変更すると、+page.svelte の​ data の​型も​自動的に​変わる

また、​path の​ params なども​自動的に​型安全に​してくれる​^https://kit.svelte.jp/docs/routing#page-page-server-js。 自分で、​型を​指定する​必要は​ない。

// 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 APIsForm 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 や​ melt-ui など​使い勝手の​いい​ものは​そこそこあるが、​まだまだ​少ない。 React Aria の​ Svelte 版が​あれば​いいのに…。

また、vueUse に​匹敵するような​便利関数詰め合わせライブラリも​まだまだ​発展中。 そも​そも​ Svelte5 で​これまでの​ store から​ rune への​移行の​過渡期である​ため、​安定して​こない​ところも​ある。 とは​いえ、​ある​程度素の​ JavaScript ライブラリが​使えるので、​そこまで​困る​ことはない。

route が​型安全に​ならない​ (SvelteKit)

fetch 関数を​使う​時や、a タグで​リンクを​貼る​ときの​文字列が​型安全に​ならない。 一応、​存在しない​pathを​指定すると、ビルド時に​エラーが​出るので 気づく​ことは​できるが、​開発中に​型安全であると​嬉しいなと​思う​(Next.jsの​typedRoutesみたいな​もの​希望)。 現状では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 と​いう​ライブラリが​ある。

tsx じゃない

個人的には​ tsx が​あまり​好きに​なれないのだが、​これは​好みの​問題ではない。 ts/tsx でない​ことの​最大の​デメリットは、​マクロ系の​ライブラリが​使えない​こと。 例えば​ unplugin と​いう、​ビルド時に​便利な​処理を​してくれる​プラグインた​ちがいるのだが、​彼らは​大抵 ts/tsx の​処理に​特化している。 その​ため、vue / svelte / astro などの​非 ts/tsx な​フレームワークの​対応が​遅れが​ちに​なる。 (個人的には​ unplugin-macros) が​使えないのが​たまに​辛いこともある​) とは​いえ、​必要な​処理を​ ts ファイルに​切り出せばいいので、​そこまで​困る​ことはない。​データの​処理で​あれば​ +page.server.ts に​逃すことも​できるので。 まあ、​本当に​必要ならば​自作するのですが…。

まとめ

ざっくり Svelte / SvelteKit の​好きなとこを​紹介した。

comment on bluesky / twitter
CC BY-NC-SA 4.0 2022-PRESENT © ryoppippi