SvelteKit新機能Snapshot, Streamingの紹介

Dongjin Kim
nextbeat-engineering
8 min readMar 28, 2023

こんにちは!

保育士バンク!の開発を担当しています、ネクストビートエンジニアのキムです。

SvelteKitは正式版の1.0が2022/12/14にリリースされ、投稿の時点(2023年3月)で3ヶ月になりましたが、その間、色々不具合の修正や機能追加が行われていました。

今回はその改修の中、SvelteKitの新機能であるSnapshotとStreamingについて共有したいと思います。

Snapshot

Snapshotは、SvelteKitの1.5バージョンから追加になった機能で、ページの移動が発生した際に、移動前のDOMの状態を保存し(capture)、元のページに戻ってきた際に、保存してあるDOMの状態に戻す(restore)機能です。

Svelteファイルにsnapshotを定義してcapture関数とrestore関数の処理を記述することで簡単に Snapshot機能が実現できます。

<script lang='ts'>
import type { Snapshot } from "./$types";
let formValue = {
name: '',
email: '',
phone: ''
}
// こちらに定義されているsnapshot関数が、ページを移動する際に実行される
export const snapshot: Snapshot = {
capture: () => formValue,
restore: (value) => formValue = value
}
</script>
<form class="content">
<div class="content-value">
<label>Name</label>
<input type="text" bind:value={formValue.name} />
</div>
<div class="content-value">
<label>E-mail</label>
<input type="text" bind:value={formValue.email} />
</div>
<div class="content-value">
<label>Telephone</label>
<input type="text" bind:value={formValue.phone} />
</div>
</form>

Snapshotが固有のキーを生成し、そのキーとマッピングするデータを定義したcapture関数の戻り値がSessionStorageに保存されます。

保存したSessionStorageデータはまた戻ってきた場合、コードに定義したrestore関数が呼び出されデータを復元します。

Snapshotの使用例

  1. ページ移動前にスクロール位置を保存して、そのページに戻ってきた時に復元したい場合
  2. 入力フォームの状態を一時的に保存して復元したい場合

ただし、仕様的にSessionStorageを使っているので、以下の場合は注意する必要があると思います。

  1. SessionStorageの仕様を超える大量データを保存しようとする場合
  2. クライアント保存になるので、セキュリティに影響が発生するようなデータの保存(パスワードなど)

画面ごとに一時的にデータを保存したい場合、複雑なDOMのイベントなどを考慮して複雑なコードを書かなくても、保存したいデータを指定するだけで実現可能なので便利な機能です。

Streaming

Streamingは、SvelteKitの1.8バージョンから追加になった機能で、Routerデータを全て生成した後にレンダリングされる既存のload関数の仕様に対して、必要に応じて一部のRouterデータを非同期でデータが生成する前にレンダリングを先に行い、非同期処理が終わった後に追加で残りの一部をレンダリングする機能です。

+page.server.ts

テストで2sで完了する処理と4sで完了する非同期処理をそれぞれ定義し、load関数の戻り値として設定します。

import type { PageServerLoad } from "./$types"

const sleep = (time: number) => new Promise(resolve => setTimeout(resolve, time))

const fetchData = async (): Promise<string> => {
return new Promise<string>(async(resolve) => {
await sleep(2000)
resolve("Data Load")
})
}

const fetchStreamData= async (): Promise<string> => {
return new Promise<string>(async(resolve) => {
await sleep(4000)
resolve("Data Stream")
})
}

export const load: PageServerLoad = () => {
return {
data: fetchData(),
streamed: {
data: fetchStreamData()
}
}
}

+page.svelte

streamとしてレンダリングするデータはsvelteファイルの中にawait句を使って画面に表示します。

<script lang='ts'>
import type { PageData } from './$types'
export let data: PageData
</script>

<!-- Data -->
<div>
{data.data}
</div>

<!-- Streamed -->
<div>
{#await data.streamed.data}
Loading Streamed..
{:then value}
{value}
{/await}
</div>

SvelteKitはページからawait句を検知し、await句中のデータをStreamingデータとしてはまだ処理が終わっていないオープンな状態でもとりあえずレンダリングします。

レンダリングが完了した後、Streamingデータの処理が完了したらその結果(then句の中)が表示されます。

これにより、PageServerLoadからPageDataの生成を全て持たなくてもレンダリングが可能になります。元々全てページのレンダリングまで4秒を待たなくてはいけない表示が結果的に2s節約できて、画面表示のUXを向上することができます。

  1. SEOに影響しないデータのレンダリング
  2. パフォーマンスに影響出そうな大量のデータをレンダリングする場合

上記の場合、レンダリングタイミングをクライアントまで持っていくことで、柔軟性を持つ画面のレンダリングが可能になりました。

終わりに

今回はSvelteKitの正式版リリースの後、追加された新機能について紹介しました。

SvelteKitはまだこれからのフレームワークで、OSSの関心度も高いですので、この先も開発者の利便性&パフォーマンスを考慮した新機能が段々できると思います。

保育士バンク!は最近Svetlteへの技術移行が完了し、これからもSvelteで構築したプラットフォームとして機能追加&改善を行っていますので、興味がある方は是非、弊社のサービスやホームページをご覧いただければと思います。

最後まで読んでいただきありがとうございました!

We are hiring!
株式会社ネクストビートでは、

「人口減少社会において必要とされるインターネット事業を創造し、ニッポンを元気にする。」
を理念に掲げ一緒に働く仲間を募集しております。

https://www.nextbeat.co.jp/recruit

--

--