SvelteKitを業務で使って半年以上になるので私が気に入っているところを紹介したいと思います。
Svelte/SvelteKitいいじゃん!使ってみたい!って思ってもらえると嬉しいです。
対象読者はNext.jsやNuxt、それらに類するフレームワークを知っている、扱ったことがある程度の方です。
そもそもSvelteってなに?
一応知らない方にも説明しておきます。
SvelteはReactやVue.jsに代表されるようなWebフロントエンドにおけるUIライブラリです。
Svelte自体は知ってる、聞いたことあるという方も多いのではないでしょうか?
この記事ではReactやVue.jsとの違いなどには触れませんので興味のある方は調べてみて下さい。
SvelteKitとは
SvelteKitはNext.jsやNuxtのようなWebアプリケーションを作成するフレームワークです。
こちらに関してもNext.jsやNuxtとの違いは特に触れません。
あえて触れるとするなら、SvelteKitはSvelteと同じコミュニティに管理されていることが前者との大きな違いです。
それによる恩恵は後ほど書きたいと思います。
1.TypeScriptの型補完が気持ち良い
公式のブログで詳しく語られているので詳細を知りたい方は上記を参照ください。
この記事ではざっくりこの機能の素晴らしさを書きたいと思います。
まずこの機能を語る上で説明しなくてはいけないことがあります。それはSvelteKitのルーティングに関してです。
SvelteKitのルーティングはファイルシステムベースのルーティングを採用していますが一見変わったルールがあります。
それはファイルの命名にルールが存在することです。
例えば/about
というパスのルーティングを作成する場合以下のようなディレクトリとファイルになります。
/about
- +page.server.ts
- +page.svelte
このように+page
から始まるファイルを作成し、.server
ではサーバーで処理されるロジックを、.svelte
はページのテンプレートを記述します。
Twitterを眺めているとこの仕様には賛否あるようですがルールがあることによって今回説明する機能が成り立っていると考えると個人的には良い判断だったのではないかと思います。
how @Rich_Harris went from "about.svelte" to "/about/+page.svelte" in @sveltejs kit 1.0 is beyond incomprehensible
— sam mueller (@samuel_mueller) May 9, 2023
literally sending devs back to nextjs
※当時の議論
https://github.com/sveltejs/kit/discussions/5748#discussioncomment-3282732
さて、話がそれてしまいましたが+page.server.ts
では次のようなコードが書かれているとします。
export const load = () => {
return {
title: 'demo app'
}
}
ページがSSRされる際やCSRでページ遷移する際にこのload関数が実行されます。
ちなみにCSRで遷移する際はHTTP APIとしてload関数が実行されサーバーで処理された結果をクライアントが受け取ります。
実行された結果は+page.svelte
のpropsとして渡されます。
<script lang="ts">
export let data
</script>
<h1>{ data.title }</h1>
サーバーのload関数の結果はexport let data
ですべて受け取れる仕様になっています。
このときVisual Studio Codeでこの変数をハイライトすると次のようにサーバーの結果の型が補完されていることがわかります。
つまり、本来ネットワーク越しで受け渡しされるデータにも関わらずTypeScriptの型として扱うことができるということです。これってすごくないですか?
2.Streaming with Promiseがイケてる
先程の+page.server.ts
の以下のように書き換えます。
export const load = () => {
return {
title: 'demo app',
streamed: {
defered: new Promise<string>((resolve) => {
setTimeout(() => {
resolve('Defered!!');
}, 5000);
})
}
}
}
streamed
オブジェクトを追加し、その中のプロパティはPromiseを返すコードになっています。
これを使って+page.svelte
も書き換えてみましょう。
<script lang="ts">
export let data
</script>
<h1>{ data.title }</h1>
{#await data.streamed.defered}
<p>Waiting.</p>
{:then value}
<p>{ value }</p>
{/await}
Promiseで返されているのでSvelteのテンプレート構文{#await}
を利用してPromiseが解決されたら展開されるといったコードになります。
このコードでは、画面描画の5秒後にWaiting.
からDefered!
に切り替わります。
使い方は簡単ですよね?
この機能により、例えば画面描画時には不要なコンポーネントをmount後にHTTP APIからfetchしてデータ取得して展開する、といったいわゆる遅延表示処理の手間が一気に減ります。
また、コンポーネントごとにfetchするといったロジックが散らばること無く、遅延する処理も全てサーバー側に集約することになります。
このStreamingの機能についてはSvelteKit 1.8から実装されています。
3.CSRでも全部aタグでOK
Svelteはコンパイラ、なんてことを聞いたことがあるかもしれません。ReactやVue.jsとは異なりライブラリのランタイムコードはクライアントにダウンロードされることはなく、ビルド時に純粋なJavaScriptとして展開される仕組みになっています。これをコンパイラだと呼ぶわけですがその恩恵は至る所に見られます。
その中でもSvelteKitではクライアントサイドでのルーティングでページ遷移をする際にHTMLのa
タグ要素で書くだけで実現してくれます。
特別なカスタムエレメントタグやコンポーネントを使う必要はありません。
CSRではなくリロードによる通常のブラウザ遷移をしたい場合はa
タグにデータ属性data-sveltekit-reload
をつけるだけです。
あるいはrel="external"
を指定することでも同様の効果になります。
純粋なHTMLだけでSPAのページ遷移を制御できる仕組みって良いですよね。
その他にも事前に次に見せたいページのデータをロードさせておくこともできたり柔軟な設計が可能です。
4.アプリケーションが最新バージョンになっているかチェックできる仕組みがある
SvelteKitではクライアント側で動いているアプリケーションが最新であるかどうかをチェックする関数が用意されています。
SSRとSPAのアプリではよくありがちな、サーバー側に新たにデプロイされたバーションとクライアント側で動いているアプリケーションのバージョンが異なることで、クライアントに意図した挙動を提供できないといったケースがあるかと思います。
その場合SvelteKitでは次のように対応できます。
<script lang="ts">
import { updated } from '$app/stores'
</script>
<div data-sveltekit-reload={$updated ? '' : 'off'}>
<slot />
</div>
こうすることによってサーバー側に新しいバージョンのアプリが検知された場合はa
リンクはすべてリロードになります。リロードになれば新しいJavaScriptファイルがダウンロードされるのでクライアント側も最新の状態になりますよね。
自前で作るにはちょっとめんどくさい仕組みが予め用意されているのはありがたいなと思います。
5.svelte-checkで安全に開発ができる
svelte-checkはSvelteKitに同梱されているCLIで利用するライブラリです。
主に以下の診断が可能です。
- 未使用のCSS
- アクセシビリティのヒント
- JavaScript/TypeScriptのコンパイルエラー
SvelteKitではnpm run check
コマンドで実行することができるので、CI環境等で利用することでプルリクエストのブロック等に活用できます。
Svelte Language Toolsが利用されているため、Visual Studio CodeのSvelte for VS Codeのプラグインを導入していればsvelte-checkと同等の機能をIDEの機能として利用できます。
最初にSvelteとSvelteKitが同じコミュニティにより管理されていると説明しましたが、それによる恩恵が強いと言える場面はまさにこのsvelte-checkだと感じています。
というのも、このsvelte-checkはSvelteKit独自の機能に対しても働きます。
例えば先程a
タグの例で紹介しましたが、a
タグが次のページの先読みを有効にしたり無効にしたりする機能があります。
<div data-sveltekit-preload-data>
<a href="/about" >About</a>
</div>
これは現在のSvelteKitでの書き方ですが、過去には以下のような書き方でした。
<div data-sveltekit-prefetch>
<a href="/about" >About</a>
</div>
この状態で現行バーションのsvelte-checkを実行すると以下のエラーが表示されます。
Error: Argument of type '{ "data-sveltekit-prefetch": boolean; }' is not assignable to parameter of type 'HTMLProps<"div", HTMLAttributes<any>>'.
Object literal may only specify known properties, and '"data-sveltekit-prefetch"' does not exist in type 'HTMLProps<"div", HTMLAttributes<any>>'. (ts)
<div data-sveltekit-prefetch>
<a href="/about" >About</a>
このように型定義に存在しないアトリビュートであることを教えてくれます。
では現行バーションで動くように変更し、IDEでマウスホバーしてみます。
なんとアトリビュートに関する説明を表示してくれます!これはまさに同じコミュニティならではという連携ぶりかと思います。
おまけ
おまけはちょっとポエムっぽいんですが、私がSvelteが好きな側面として、そもそもReact(というよりJSX)が苦手だったということがあるかと思います。個人的なイメージなんですが、ReactはJavaScriptの世界でHTMLを掌握するといった感じに見えてしまいちょっと苦手意識があったんですが、Svelteはその逆でHTMLがもしインタラクティブになったとしたらどんなDSLになるか、といった発想が出発点となっているのでHTMLを拡張した世界と捉えられるかなと思います。クライアントサイドのルーティングをa
タグで行える点なども、まさにHTMLそのものを使っていて良いなと思っています。
私が普段使ってない機能がまだまだ多く、SvelteKitはプログレッシブエンハンスメントなどにも力を入れていてJavaScriptが動かない環境でも適切に表示できるようにする仕組みなども備えています。そのへんはこの記事で紹介できていないので気になった方はドキュメントを一読いただくことをおすすめします。
この記事を読んでSvelte/SvelteKitいいなと思ったらぜひ触ってみてください!新規採用する際の比較検討の対象に加えたり、実際に採用される例が増えてくれると嬉しいです!
また、すでにSvelteを使っている方はぜひコメント等で好きな機能や活用シーンなんかを教えてくれると嬉しいです!(別途記事を書いてくださるともっと嬉しいです!)