SvelteKit のデータローディング load 関数まわりのうごきを整理する

![Screenshot 2024-07-22 at 13.33.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95370/958a435f-5c75-9ad3-648e-522dccd9544a.png)


Next.js や Nuxt などのSSR対応フレームワークと同様に、SvelteKit もデータの読み込みのための標準の方法を備えています（SSRのハイドレーションをうまくやるためですね）。

SvelteKit はこのデータローディングの面でもファイルベースのルーティングを用いていて、各ルートの `+page.ts`, `+layout.ts`, `+page.server.ts`, `+layout.server.ts` のファイルで、データ取得処理を `load()` 関数として記述することで、ページコンポーネントやレイアウトコンポーネントにデータを供給できます。SSRのための裏方の作業も勝手にやってくれます。

...というのは、皆さんご存知だとして、ここではもう少し細かい話を整理してみます。


**公式ドキュメントは以下にあります**：

- 英語: https://kit.svelte.dev/docs/load
- 日本語: https://kit.svelte.jp/docs/load

## `+page.ts` と `+page.server.ts` の使い分け

基礎的ですが、一応触れておきます。

**Universal vs server**: https://kit.svelte.dev/docs/load#universal-vs-server

- `+page.ts` と `+layout.ts` には、ユニバーサルな（＝サーバ上でもブラウザ上でも実行できる）データ取得処理を記述します。サーバサイドレンダリング時にはサーバで実行され、ブラウザでのページ遷移時などにはブラウザで実行されます。ユニバーサルな処理はこちらに書くべきです。
- 一方で `+page.server.ts` と `+layout.server.ts` は常にサーバ上で実行されます。必ずサーバ上で実行されなければならない処理はこちらに記述します。

## 「Provided `fetch` function を使え」

- https://kit.svelte.dev/docs/load#making-fetch-requests

Load関数内では、ネイティブの `fetch` 関数ではなく、SvelteKit が提供する `fetch` 関数を使います。 load 関数の引数として渡される `fetch` 関数を受け取って使います。

```typescript:+page.ts
export const load: PageLoad = async ({ fetch, params }) => {
	const res = await fetch(`/api/items/${params.id}`);
	const item = await res.json();
	return { item };
};
```

この特別な fetch には SSR を支援するための工夫がなされています：

- URLの invalidation に対応する（後述）
- SvelteKitが受けた Cookie と Authorization ヘッダが fetch 先にも自動で転送される（APIに認証情報を渡すのに便利）
- URLとして相対URLが使える（ユニバーサルな記述をするのに便利）
- SSRのハイドレーションにおいて、サーバでfetchした内容もHTMLに焼き込まれて、ブラウザ側でも同じ内容が取得される。これにより無駄なリクエストがサーバとクライアントの両方で走らない。
- などなど。詳しくは https://kit.svelte.dev/docs/load#making-fetch-requests

## load 関数の再実行

Load関数は、URLの変化などに応じて、必要な Load 関数だけが自動で再実行される仕組みになっています（SvelteKit がこれらの変化を追跡する機能を備えています）。詳細はドキュメントを参照してください。 https://kit.svelte.dev/docs/load#rerunning-load-functions

### 手動での invalidation

しかしながら、Load関数のなかで取得した内容を、ページ遷移などをせずに再読み込みさせたい場面もあります。その場合は `invalidate` 関数が使えます。 https://kit.svelte.dev/docs/load#rerunning-load-functions-manual-invalidation

fetch したURLを指定して invalidate するのが基本的な使い方です。そのURLに依存しているLoad関数がすべて再実行されます。

```typescript
    invalidate('https://api.example.com/random-number');
    invalidate('app:random');  // カスタムの依存関係も作れる → depends を参照
    invalidate((url) => url.href.includes('random-number'));
    invalidateAll();
  ````

## 各Load関数間の data の流れ

様々な load 関数がどのような流れで働くかは、少し分りづらいところがあるように思います。

各 load 関数の間での data の流れについて、いくつかの要点を整理してみます：

- `+layout.server.ts`, `+layout.ts` で取得したデータは、その配下（子レイアウトや子ページ）にあるすべての `+layout.svelte` と `+page.svelte` でも参照できます。この振る舞いは重要かもしれません。
    - 同じプロパティ名で衝突した場合は下位の load 関数によるものが優先される、とされています。勝手にオブジェクトの子がマージされたりはしません。注意。
- `+layout.server.ts` で取得したデータは対応する `+layout.ts` で参照できます。同様に、`+page.server.ts` で取得したデータは対応する `+page.ts` で参照できます。
- **`parent()`** — Load関数で `await parent()` すると、これらの制限を超えて、親の `+layout.ts` や `+layout.server.ts` の data を待機して取得できます。ただしデータ取得の並列性を阻害して待機することになるn、極力使用を避けるように注意されています。
    - https://kit.svelte.dev/docs/load#using-parent-data
- （認証などの目的で）すべてのリクエストをフックする `hooks.server.ts` を使う場合に `hooks.server.ts` で得られたデータを `+(layout|page).server.ts` にも送りたい場合は、リクエストローカルな `event.locals` を介して送ることができます。

適当な図にすると、以下のようになるでしょうか（`parent()`は除外）：

![Screenshot 2024-07-22 at 13.33.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95370/958a435f-5c75-9ad3-648e-522dccd9544a.png)