Svelte/SvelteKit の地味に好きなところ
普段から Svelte
/ SvelteKit
を使っているので、その地味に好きなところを紹介します。
また、微妙なところも最後に紹介します。
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
が便利なところ
Svelte
では、通常の class='foo'
に加えて、class:foo
という書き方ができる[1]。
加えて、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
が不要になった[2]。
style
も style:name={foo}
のように書くことができる[3]。
<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が安定しないのでご了承ください。
日本語資料:
英語資料(このdemoが本当にわかりやすいので、字幕をつけて見てほしい):
自分は昨年発表されてからずっと rune
を使っているが、かなり使いやすい。
正直 Svelte4
以前には戻れない。それくらい書き味が良い。
見た目は Vue
の Ref
であり、省略記法な React
の useState
であるが、コンパイラを経由することで書き心地とパフォーマンスを両立している。
他にも snippet
構文が最高だったり、$props()
を用いた型安全な props の宣言が可能になったりと、 Svelt5
はとても良いものだが、いかんせんリリースが遅れに遅れているのが残念。
年度末の 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
SvelteKit
では +page.server.ts
と +page.svelte
というファイルを用いて、サーバーの処理とクライアントの処理を分離することができる。
Remix
でいうところの loader
と component
に相当するものである(厳密に言えば +page.server.ts
には actions
も書ける)。
すごいのは、この2つのファイルにまたがっているデータのやりとりが自動的に型安全になることである。
typeof load
みたいなことをする必要がなく、+page.server.ts
の load
関数の戻り値を変更すると即座に +page.svelte
の data
の型も変わる。
+page.server.ts
の戻り値を変更すると、+page.svelte
の data
の型も自動的に変わる
また、path の params なども自動的に型安全にしてくれる[4]。
自分で、型を指定する必要はない。
// 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 標準技術を駆使している。
例えば、fetch
関数、Request
クラス、Stream APIs
、Form Actions
などなど。
ここ1年ほど React
界隈でやっと 'Form Actions' やら Progressive Enhancement
が話題になってきたが、SvelteKit
はそれに先駆けて Web 標準を謳っていた印象がある。
これについても過去に記事を書いたので、興味があれば読んでみてください。
Server / Client がファイルベースで分離されているところ
先ほども述べたが、SvelteKit
では +page.server.ts
と +page.svelte
というファイルを用いて、サーバーの処理とクライアントの処理を分離することができる。
また、.server
がついているファイルはクライアント側で読み込むことができない。
環境変数も、PUBLIC_
がついているものだけがクライアント側で読み込むことができる。
クライアント側に不要な情報が漏れることを防ぐ仕組みができていることは、開発する上で安心感を与えてくれる。
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 を型安全にすることができるが、これが標準であると嬉しい。
Server Endpoint の Response が型安全にならない (SvelteKit)
Server endpoint の結果も型安全になっていない(普通に fetch
関数で叩いてデータを取得しているだけなので)。
現状、Server Endpoint の型安全を保証したいなら、trpc
や Hono RPC
と組み合わせる必要がある。
上で述べた通り、SvelteKit
は色々と型安全を頑張っているので、もう一踏ん張りしてほしい。
(そもそも Server Endpoint を使う必要はあるのか、全部 +page.server.ts
の load
関数内に書いてしまえばいいのでは? という意見もある)
tsx
じゃない
個人的には tsx
があまり好きになれないのだが、これは好みの問題ではない。
ts
/tsx
でないことの最大のデメリットは、マクロ系のライブラリが使えないこと。
例えば unplugin
という、ビルド時に便利な処理をしてくれるプラグインたちがいるのだが、彼らは大抵 ts
/tsx
の処理に特化している。
そのため、vue
/ svelte
/ astro
などの非 ts
/tsx
なフレームワークの対応が遅れがちになる。
(個人的には unplugin-macros
) が使えないのがたまに辛いこともある)
とはいえ、必要な処理を ts
ファイルに切り出せばいいので、そこまで困ることはない。データの処理であれば +page.server.ts
に逃すこともできるので。
まあ、本当に必要ならば自作するのですが...。
まとめ
ざっくり Svelte
/ SvelteKit
の好きなとこを紹介した。
Discussion
記事を公開していただき、ありがとうございます!
質問なのですが、「直感的に書けるところ」というのはどのような点を指しているのでしょうか?
まず前提として私は「Reactはある程度書けるがSvelteには全く詳しくない、でも興味は持っている」という状態です。
私の印象だと、
など、他ではあまり見られない書き方が多くいです。
なかなか非直感的で辛そうだなーと勝手ながら思っていたのですが、意外と触ってみるとそんなことはないのでしょうか?
Svelteってユーザーからの評価はかなり高い印象があって、ずっと興味がありました。
個人的にReact(JSX)が好きなのもあり、Svelteの書き方が気持ち悪く見えるのがネックで後回しにしてたのですが、意外にそんなことないというのであれば試してみたいなと思った感じです...!
コメントありがとうございます!
「直感的に書けるところ」 という意味の真意は
といったところです。
3つめの部分に関して具体的なコードを書くと、Reactでは
と書くところを、Svelteでは
で書ける、ということです(Reactを悪く言う意図はありません)。
とはいえ、
export
を多用する、$:
でリアクティブになるというのは、Svelte に初めて触れる方からはとっつきにくいと言われる意見ですね。自分はなれてしまいましたが。
ただ、Svlete4までのReactivityには一部の直感さを優先した結果起きてしまった非直感さも多くあります。
例えば、top-levelで宣言した変数しかリアクティブにならないこともその一つです。
加えて、
export let
とかは受け手側は値を変更しないのにlet
を使わざるをえないのが違和感がありそうです(実際自分もそうでした)。それらはだいぶSvelte5で解消されると思います。
上に示したコードは、Svelte5で書くと
Repl
と書けるようになります。
また、
export let
の代わりに$props
が導入されるので、React に似た感覚で props を受け取ることができるようになります。参考 Repl
なので、Svelte5 になると、React になれている方も、Svelte を始める心理的なハードルが下がると思います。
ただ、まだ正式リリースになっていないので、チュートリアルが古いままなので勉強しづらいですね....
Svelte5のリリースはおそらく数ヶ月先だと思いますが...
ここら辺の資料を眺めていただければなと思います!
返信ありがとうございます!
そうですよね。同じ理由で触るのを躊躇ってました。
なるほど!Svelte5のruneについて、ずいぶん前から知ってはいましたが、「なんとなく使いやすくなるんだなー」程度の浅い認識しかありませんでした...。
自分がSvelteを避けてた理由の大半が解決されているのであれば、触ってみたくなりますね。
改めて、わざわざ回答していただきありがとうございました!
触ってみます!