こんにちは。よっしーです(^^)
今日は、SvelteKitでのデータ読み込みについて解説しています。
背景
SvelteKitでのデータ読み込みについて調査する機会がありましたので、その時の内容を備忘として記事に残しました。
SvelteKitでのデータ読み込み
+page.svelte コンポーネント (およびそれに含まれる +layout.svelte コンポーネント) をレンダリングする前に、多くの場合、データを取得する必要があります。これは、ロード関数を定義することによって行われます。
ロード関数の再実行
SvelteKit は、ナビゲーション中に不必要に再実行されることを避けるために、各ロード関数の依存関係を追跡します。 たとえば、次のような 2 つのload関数があるとします。
// src/routes/blog/[slug]/+page.server.js
import * as db from '$lib/server/database';
/** @type {import('./$types').PageServerLoad} */
export async function load({ params }) {
return {
post: await db.getPost(params.slug)
};
}
// src/routes/blog/[slug]/+layout.server.js
import * as db from '$lib/server/database';
/** @type {import('./$types').LayoutServerLoad} */
export async function load() {
return {
posts: await db.getPostSummaries()
};
}
+page.server.js
のload
関数は、URLのスラッグ(params.slug
)が変わると再実行されますが、+layout.server.js
の関数は、データが依然有効なため再実行されません。
親のload
関数が再実行された場合、await parent()
を呼び出すload
関数も再実行されます。
依存関係の追跡はload
関数が返された後には適用されません。例えば、ネストされたプロミス内でparams.x
にアクセスしても、params.x
が変更された際に関数は再実行されません。
検索パラメータは、URLの残りの部分とは独立して追跡されます。event.url.searchParams.get("x")
にアクセスするload
関数は、x
の値が変わると再実行されますが、他のパラメータの変更では再実行されません。
依存関係の追跡を解除する
まれに、依存関係追跡メカニズムから何かを除外したい場合があります。これは、提供されている untrack 関数を使用して行うことができます。
// src/routes/+page.js
/** @type {import('./$types').PageLoad} */
export async function load({ untrack, url }) {
// Untrack url.pathname so that path changes don't trigger a rerun
if (untrack(() => url.pathname === '/')) {
return { message: 'Welcome!' };
}
}
手動無効化
また、現在のページに適用されるロード関数を再実行するには、 urlに依存するすべてのロード関数を再実行するinvalidate(url)と、 すべてのロード関数を再実行するinvalidateAll()を使用します。サーバのロード関数は、クライアントに秘密が漏れるのを避けるために、 取得した url に自動的に依存することはありません。
fetch(url)またはdepends(url)を呼び出した場合、ロード関数は urlに依存します。urlは[a-z]:で始まるカスタム識別子であることに注意してください:
// src/routes/random-number/+page.js
/** @type {import('./$types').PageLoad} */
export async function load({ fetch, depends }) {
// load reruns when `invalidate('https://api.example.com/random-number')` is called...
const response = await fetch('https://api.example.com/random-number');
// ...or when `invalidate('app:random')` is called
depends('app:random');
return {
number: await response.json()
};
}
// src/routes/random-number/+page.svelte
<script>
import { invalidate, invalidateAll } from '$app/navigation';
/** @type {import('./$types').PageData} */
export let data;
function rerunLoadFunction() {
// any of these will cause the `load` function to rerun
invalidate('app:random');
invalidate('https://api.example.com/random-number');
invalidate(url => url.href.includes('random-number'));
invalidateAll();
}
</script>
<p>random number: {data.number}</p>
<button on:click={rerunLoadFunction}>Update random number</button>
ロード関数はいつ再実行されますか?
load
関数が再実行される状況は以下の通りです:
- 値が変更された
params
のプロパティを参照している場合 - 値が変更された
url
のプロパティ(url.pathname
やurl.search
など)を参照している場合(request.url
のプロパティは追跡されません) url.searchParams.get(...)
,url.searchParams.getAll(...)
,url.searchParams.has(...)
を呼び出し、対象のパラメータが変更された場合await parent()
を呼び出し、親のload
関数が再実行された場合- 子の
load
関数がawait parent()
を呼び出して再実行中で、親がサーバーサイドのload
関数である場合 fetch
またはdepends
を通じて特定のURLへの依存関係を宣言し、そのURLがinvalidate(url)
でマークされた場合invalidateAll()
によってすべてのアクティブなload
関数が強制的に再実行された場合
params
とurl
は、リンクのクリック、フォームの操作、goto
の呼び出し、またはredirect
によって変更される可能性があります。
load
関数の再実行は、対応する+layout.svelte
または+page.svelte
内のdata
プロパティを更新しますが、コンポーネントの再作成は行いません。内部状態は保持されます。必要に応じて、afterNavigate
コールバック内でリセットを行うか、コンポーネントを{#key ...}
ブロックでラップすることができます。
認証への影響
データのロードに関する以下の特徴は、認証チェックに重要な影響を与えます:
- レイアウトの
load
関数は、子ルート間のクライアントサイドナビゲーションなど、すべてのリクエストで実行されるわけではありません。 await parent()
が呼び出されない限り、レイアウトとページのload
関数は同時に実行されます。レイアウトのload
関数がエラーを投げても、ページのload
関数は実行されますが、クライアントは返されたデータを受け取りません。
保護されたコードの前に認証チェックを確実に行うための戦略がいくつかあります:
データのウォーターフォールを防ぎ、レイアウトのload
キャッシュを保持するには:
- フックを使用して、
load
関数が実行される前に複数のルートを保護する - ルート固有の保護には、
+page.server.js
のload
関数内で直接認証ガードを使用する
+layout.server.js
に認証ガードを配置すると、すべての子ページが保護されたコードの前にawait parent()
を呼び出す必要があります。すべての子ページがawait parent()
から返されるデータに依存しない限り、他のオプションの方がパフォーマンスが高くなります。
おわりに
今日は、 SvelteKitでのデータ読み込みについて解説しました。
何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)
コメント