This is the developer documentation for SvelteKit. # イントロダクション ## 始める前に > [!NOTE] Svelte や SvelteKit が初めてなら、こちらの[インタラクティブなチュートリアル](/tutorial/kit)をチェックしてみることをおすすめします。 > > 行き詰まったら、[Discord chatroom](/chat) でヘルプを求めてください。(日本語翻訳版 追記:上記のDiscordはSvelte本体のもので、英語でコミュニケーションが行われています。もし日本語で質問したり交流したいのであれば、[Svelte日本のDiscord](https://discord.com/invite/YTXq3ZtBbx)にどうぞ!) ## SvelteKitとは SvelteKit は、[Svelte](../svelte) を使用して堅牢でハイパフォーマンスな web アプリケーションを迅速に開発するためのフレームワークです。もしあなたが React 界隈から来たのであれば、SvelteKit は Next に似ているものです。Vue 界隈から来たのであれば、Nuxt に似ています。 SvelteKit で構築することのできるアプリケーションの種類については、[FAQ](faq#What-can-I-make-with-SvelteKit) をご覧ください。 ## Svelteとは 手短に言えば Svelte は、ナビゲーションバーやコメントセクション、コンタクトフォームなど、ユーザーがブラウザで見たり操作したりするユーザーインターフェースコンポーネントを書く方法です。Svelte コンパイラは、コンポーネントを、ページの HTML をレンダリングする実行可能な JavaScriptと、ページのスタイリングをする CSS に変換します。このガイドの残りの部分を理解するのに Svelte を知っておく必要はありませんが、もし知っていれば役に立つでしょう。より詳しく知りたい場合は、[Svelte のチュートリアル](/tutorial) をご覧ください。 ## SvelteKit vs Svelte Svelte は UI コンポーネントをレンダリングします。Svelte だけでも、コンポーネントを組み合わせてページ全体をレンダリングすることは可能ですが、アプリ全体を書くには Svelte だけでなく、他のものも必要です。 SvelteKit は、モダンなベストプラクティスに従い、開発する上での一般的な課題に対するソリューションを提供し、Web アプリケーションを構築するのに役立ちます。例えばリンクをクリックしたときに UI を更新してくれる[ルーター(router)](glossary#Routing)のような基本的な機能はもちろん、より高度な機能まで提供します。その幅広い機能のリストには、必要最小限のコードのみを読み込むための[ビルド最適化](https://vitejs.dev/guide/features.html#build-optimizations)、[オフラインサポート](service-workers)、[プリロード(preloading)](link-options#data-sveltekit-preload-data) (ユーザーがナビゲーションを開始する前にページを事前読み込みする)、[柔軟な設定が可能なレンダリング](page-options) (アプリのある部分は [SSR](glossary#SSR) によってサーバー上でレンダリングさせたり、また別の部分はブラウザで[クライアントサイドレンダリング](glossary#CSR)させたり、また別の部分はビルド時に[プリレンダリング](glossary#Prerendering)させたりすることが可能)、[イメージ最適化](images)、その他様々なものがあります。一般的に、最新のベストプラクティスを駆使してアプリを構築することは非常に複雑ですが、SvelteKit の場合は SvelteKit が全ての退屈な作業を行ってくれるので、あなたはクリエイティブな作業に専念することができます。 [Svelte plugin](https://github.com/sveltejs/vite-plugin-svelte) で [Vite](https://vitejs.dev/) を動かして [Hot Module Replacement (HMR)](https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/config.md#hot) を行うことで、コードの変更を即座にブラウザに反映し、非常に高速でフィーチャーリッチな開発体験を提供します。 # プロジェクトを作成する SvelteKit アプリの構築を始めるのに最も簡単な方法は `npx sv create` を実行することです: ```bash npx sv create my-app cd my-app npm install npm run dev ``` 最初のコマンドでは、TypeScript などの基本的なツールをセットアップするかどうか選択して、`my-app` ディレクトリに新しいプロジェクトを生成します。追加のツールの設定に関するポイントについては [Integrations](./integrations) をご覧ください。それ以降のコマンドでは、依存関係をインストールし、[localhost:5173](http://localhost:5173) でサーバーを起動します。 SvelteKit には2つの基本的なコンセプトがあります: - アプリの各ページは [Svelte](../svelte) コンポーネントです - プロジェクトの `src/routes` ディレクトリにファイルを追加することで、ページを作成できます。これらはサーバーでレンダリングされるのでユーザーの最初のアクセスの際に可能な限り速く表示されるようになり、それからクライアントサイドのアプリに引き継がれます ファイルを編集して、どのように動作するのか確かめてみてください。 ## エディタのセットアップ [Visual Studio Code (通称 VS Code)](https://code.visualstudio.com/download) と [Svelte extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode) のご使用をおすすめしますが、[他にも数多くのエディタをサポートしています](https://sveltesociety.dev/resources#editor-support)。 # プロジェクト構成 一般的な SvelteKit プロジェクトはこのような構成です: ```bash my-project/ ├ src/ │ ├ lib/ │ │ ├ server/ │ │ │ └ [your server-only lib files] │ │ └ [your lib files] │ ├ params/ │ │ └ [your param matchers] │ ├ routes/ │ │ └ [your routes] │ ├ app.html │ ├ error.html │ ├ hooks.client.js │ ├ hooks.server.js │ └ service-worker.js ├ static/ │ └ [your static assets] ├ tests/ │ └ [your tests] ├ package.json ├ svelte.config.js ├ tsconfig.json └ vite.config.js ``` また、`.gitignore`、`.npmrc` などの共通ファイルもあります (もし `npx sv create` の実行時にオプションを選択した場合は `.prettierrc` や `eslint.config.js` などもあるでしょう)。 ## プロジェクトファイル ### src `src` ディレクトリには、プロジェクトの中身が格納されます。`src/routes` と `src/app.html` 以外は全てオプションです。 - `lib` にはあなたのライブラリのコード (ユーティリティやコンポーネント) を格納します。格納されたコードは [`$lib`]($lib) エイリアスを使用してインポートしたり、[`svelte-package`](packaging) を使用して配布用にパッケージングすることができます。 - `server` にはあなたのサーバー専用のライブラリのコードを格納します。格納されたコードは [`$lib/server`](server-only-modules) エイリアスを使用してインポートすることができます。SvelteKit はこれをクライアントコードにインポートされるのを防ぎます。 - `params` にはアプリに必要な [param matchers](advanced-routing#Matching) を格納します - `routes` にはアプリケーションの [ルート(routes)](routing) を格納します。単一のルート(route)でしか使われないコンポーネントをここに置くこともできます - `app.html` はページのテンプレートで、以下のプレースホルダーを含む HTML document です: - `%sveltekit.head%` — アプリに必要な `` 要素や `

{data.title}

{@html data.content}
``` > [!LEGACY] > `PageProps` was added in 2.16.0. In earlier versions, you had to type the `data` property manually with `PageData` instead, see [$types](#\$types). > > Svelte 4 では、代わりに `export let data` を使用します > [!NOTE] SvelteKit では、ルート(routes)間のナビゲーションに、フレームワーク固有の `` コンポーネントではなく、`` 要素を使用します。 ### +page.js ページではたびたび、レンダリングの前になんらかのデータを読み込む必要があります。これに対応するため、`load` 関数をエクスポートする `+page.js` モジュールを追加しています: ```js /// file: src/routes/blog/[slug]/+page.js import { error } from '@sveltejs/kit'; /** @type {import('./$types').PageLoad} */ export function load({ params }) { if (params.slug === 'hello-world') { return { title: 'Hello world!', content: 'Welcome to our blog. Lorem ipsum dolor sit amet...' }; } error(404, 'Not found'); } ``` この関数は `+page.svelte` とともに実行されます。サーバーサイドレンダリング中はサーバーで実行され、クライアントサイドナビゲーション中はブラウザで実行されます。API の詳細は [`load`](load) をご参照ください。 `+page.js` では、`load` だけでなくページの動作(behaviour)を設定するための値をエクスポートすることができます: - `export const prerender = true` または `false` または `'auto'` - `export const ssr = true` または `false` - `export const csr = true` または `false` これらに関するより詳しい情報は [page options](page-options) をご覧ください。 ### +page.server.js `load` 関数をサーバー上でのみ実行できるようにしたい場合 — 例えば、データベースからデータを取得したり、API キーのようなプライベートな[環境変数]($env-static-private)にアクセスしたりする必要がある場合 — `+page.js` を `+page.server.js` にリネームし、`PageLoad` 型を `PageServerLoad` に変更します。 ```js /// file: src/routes/blog/[slug]/+page.server.js // @filename: ambient.d.ts declare global { const getPostFromDatabase: (slug: string) => { title: string; content: string; } } export {}; // @filename: index.js // ---cut--- import { error } from '@sveltejs/kit'; /** @type {import('./$types').PageServerLoad} */ export async function load({ params }) { const post = await getPostFromDatabase(params.slug); if (post) { return post; } error(404, 'Not found'); } ``` クライアントサイドナビゲーション中は、SvelteKit はサーバーからこのデータを読み込みます。つまり、その戻り値は [devalue](https://github.com/rich-harris/devalue) によってシリアライズできなければならないということです。この API の詳細については [`load`](load) をご参照ください。 `+page.js` のように、`+page.server.js` は [page options](page-options) (`prerender`、`ssr`、`csr`) をエクスポートできます。 また、`+page.server.js` ファイルは _actions_ をエクスポートできます。`load` がサーバーからデータを読み取る場合、`actions` は `
` 要素を使用してサーバーにデータを書き込むことができます。これらの使い方を学ぶには、[form actions](form-actions) セクションをご参照ください。 ## +error `load` 中にエラーが発生した場合、SvelteKit はデフォルトのエラーページをレンダリングします。`+error.svelte` を追加することで、ルート(route) ごとにエラーページをカスタマイズすることができます: ```svelte

{page.status}: {page.error.message}

``` > [!LEGACY] > `$app/state` was added in SvelteKit 2.12. If you're using an earlier version or are using Svelte 4, use `$app/stores` instead. SvelteKit は、ツリーを上がって (walk up the tree) 最も近いエラー境界 (error boundary) を探します — もし上記のファイルが存在しない場合は、デフォルトのエラーページをレンダリングする前に `src/routes/blog/+error.svelte` を探しに行き、その次に `src/routes/+error.svelte` を探します。もしそれも失敗した場合は (または、最上位の `+error` の '上に' 位置する最上位の `+layout` の `load` 関数からエラーがスローされた場合)、SvelteKit は静的なフォールバックエラーページをレンダリングします。これは `src/error.html` ファイルを作成することでカスタマイズ可能です。 `+layout(.server).js` の `load` 関数の内側でエラーが発生した場合、ツリーの中で最も近くにあるエラー境界はそのレイアウトの上位にある `+error.svelte` ファイルです (隣ではありません)。 ルート(route)が見つからない場合 (404)、`src/routes/+error.svelte` (または、もしこのファイルが存在しない場合はデフォルトのエラーページ) が使われます。 > [!NOTE] エラーが [`handle`](hooks#Server-hooks-handle) の内側や [+server.js](#server) リクエストハンドラ の内側で発生した場合は、`+error.svelte` は使用されません。 エラーハンドリングに関する詳細は [こちら](errors) からお読み頂けます。 ## +layout これまで、ページを完全に独立したコンポーネントとして扱ってきました — ナビゲーションを行うと、既存の `+page.svelte` コンポーネントが破棄され、新しいページコンポーネントで置き換えられます。 しかし多くのアプリでは、トップレベルのナビゲーションやフッターのように _全ての_ ページで表示されるべき要素があります。全ての `+page.svelte` にそれらを繰り返し配置する代わりに、_レイアウト(layouts)_ に配置することができます。 ### +layout.svelte 全てのページに適用するレイアウトを作成するには、`src/routes/+layout.svelte` というファイルを作成します。デフォルトのレイアウト (あなたが作成していない場合に SvelteKit が使用するもの) は以下のようなものです… ```svelte {@render children()} ``` …しかし、お望みのマークアップ(markup)、スタイル(styles)、動作(behaviour)を追加することができます。唯一の要求事項は、コンポーネントにページコンテンツのための `@render` タグを含めることです。例えば、nav bar を追加してみましょう: ```svelte
{@render children()} ``` `/`、`/about`、`/settings` のためのページを作成する場合… ```html /// file: src/routes/+page.svelte

Home

``` ```html /// file: src/routes/about/+page.svelte

About

``` ```html /// file: src/routes/settings/+page.svelte

Settings

``` … nav は常に表示され、3つのページのための a 要素をそれぞれクリックしても `

` が置き換わるだけです。 レイアウトは _ネスト_ させることができます。例えば、単一の `/settings` ページだけでなく、`/settings/profile` や `/settings/notifications` のような共有のサブメニューを持つネストしたページがあるとします (実例としては、[github.com/settings](https://github.com/settings) をご参照ください)。 `/settings` 配下のページにのみ適用されるレイアウトを作成することができます (トップレベルの nav を持つ最上位のレイアウト(root layout)を継承しています): ```svelte

Settings

{@render children()} ``` > [!LEGACY] > `LayoutProps` was added in 2.16.0. In earlier versions, you had to [type the properties manually instead](#\$types). `data` がどのように入力されるかは、すぐ下の次のセクションにある `+layout.js` の例を見ればわかります。 デフォルトでは、各レイアウトはその上にあるレイアウトを継承します。そうしたくない場合は、[advanced layouts](advanced-routing#Advanced-layouts) が役に立つでしょう。 ### +layout.js `+page.svelte` が `+page.js` からデータを読み込むように、`+layout.svelte` コンポーネントは `+layout.js` の [`load`](load) 関数からデータを取得することができます。 ```js /// file: src/routes/settings/+layout.js /** @type {import('./$types').LayoutLoad} */ export function load() { return { sections: [ { slug: 'profile', title: 'Profile' }, { slug: 'notifications', title: 'Notifications' } ] }; } ``` `+layout.js` が [page options](page-options) (`prerender`、`ssr`、`csr`) をエクスポートする場合、それは子ページのデフォルトとしても使用されます。 レイアウトの `load` 関数から返されるデータは全ての子ページで利用することができます: ```svelte ``` > [!NOTE] しばしば、ページ間を移動しているときにレイアウトデータが変更されないことがあります。SvelteKit は必要に応じてインテリジェントに [`load`](load) 関数を再実行します。 ### +layout.server.js サーバー上でレイアウトの `load` 関数を実行するためには、それを `+layout.server.js` に移動し、`LayoutLoad` 型を `LayoutServerLoad` に変更します。 `+layout.js` と同様に、`+layout.server.js` では [page options](page-options) — `prerender`、`ssr`、`csr` をエクスポートすることができます。 ## +server ページと同様に、`+server.js` ファイル (よく 'API ルート(API route)' または 'エンドポイント(endpoint)' とも呼ばれる) でルート(routes) を定義でき、これによってレスポンスを完全にコントロールすることができます。`+server.js` ファイル は `GET`、`POST`、`PATCH`、`PUT`、`DELETE`、`OPTIONS`、`HEAD` といった HTTP verbs に対応する関数をエクスポートします。これは `RequestEvent` を引数に取り、[`Response`](https://developer.mozilla.org/ja/docs/Web/API/Response) オブジェクトを返します。 例えば、`GET` ハンドラを使用した `/api/random-number` ルート(route)を作成できます: ```js /// file: src/routes/api/random-number/+server.js import { error } from '@sveltejs/kit'; /** @type {import('./$types').RequestHandler} */ export function GET({ url }) { const min = Number(url.searchParams.get('min') ?? '0'); const max = Number(url.searchParams.get('max') ?? '1'); const d = max - min; if (isNaN(d) || d < 0) { error(400, 'min and max must be numbers, and min must be less than max'); } const random = min + Math.random() * d; return new Response(String(random)); } ``` `Response` の第一引数には [`ReadableStream`](https://developer.mozilla.org/ja/docs/Web/API/ReadableStream) を指定することができ、大量のデータをストリームしたり、server-sent events を作成したりすることができます (AWS Lambda のような、レスポンスをバッファするプラットフォームにデプロイする場合は除きます)。 便宜上、`@sveltejs/kit` の [`error`](@sveltejs-kit#error)、[`redirect`](@sveltejs-kit#redirect)、[`json`](@sveltejs-kit#json) メソッドを使用することは可能です (ただし、使用する必要はありません)。 エラーがスローされる場合 (`error(...)` の場合でも、予期せぬエラーの場合でもどちらでも)、レスポンスは `Accept` ヘッダーに応じて、そのエラーの JSON 表現か、`src/error.html` でカスタマイズすることができるフォールバックエラーページとなります。この場合、[`+error.svelte`](#error) コンポーネントはレンダリングされません。エラーハンドリングに関する詳細は [こちら](errors) からお読み頂けます。 > [!NOTE] `OPTIONS` ハンドラを作成する場合、Vite が `Access-Control-Allow-Origin` ヘッダーと `Access-Control-Allow-Methods` ヘッダーを注入することにご注意ください。本番環境では、あなたが明示的に追加しない限り注入されないはずです。 > [!NOTE] `+layout` files have no effect on `+server.js` files. If you want to run some logic before each request, add it to the server [`handle`](hooks#Server-hooks-handle) hook. ### Receiving data `+server.js` ファイルは、`POST`/`PUT`/`PATCH`/`DELETE`/`OPTIONS`/`HEAD` ハンドラをエクスポートすることで、完全な API を作成することができます: ```svelte + = {total} ``` ```js /// file: src/routes/api/add/+server.js import { json } from '@sveltejs/kit'; /** @type {import('./$types').RequestHandler} */ export async function POST({ request }) { const { a, b } = await request.json(); return json(a + b); } ``` > [!NOTE] 一般的には、ブラウザからサーバーにデータを送信する方法としては [form actions](form-actions) のほうがより良い方法です。 > [!NOTE] `GET` ハンドラがエクスポートされている場合、`HEAD` リクエストは `GET` ハンドラのレスポンスボディの `content-length` を返します。 ### Fallback method handler `fallback` ハンドラをエクスポートすると、ハンドリングされていないリクエスト (`+server.js` にそれ専用のエクスポートがない `MOVE` などのメソッドを含む) にマッチします。 ```js // @errors: 7031 /// file: src/routes/api/add/+server.js import { json, text } from '@sveltejs/kit'; export async function POST({ request }) { const { a, b } = await request.json(); return json(a + b); } // This handler will respond to PUT, PATCH, DELETE, etc. /** @type {import('./$types').RequestHandler} */ export async function fallback({ request }) { return text(`I caught your ${request.method} request!`); } ``` > [!NOTE] `HEAD` リクエストの場合、`fallback` ハンドラより `GET` ハンドラが優先されます。 ### Content negotiation `+server.js` ファイルは `+page` ファイルと同じディレクトリに置くことができ、これによって同じルート(route)がページにも API エンドポイントにもなるようにすることができます。これがどちらなのか判断するために、SvelteKit は以下のルールを適用します: - `PUT`/`PATCH`/`DELETE`/`OPTIONS` リクエストは、ページには適用されないため、常に `+server.js` で処理されます。 - `GET`/`POST`/`HEAD` リクエストは、`accept` ヘッダーが `text/html` を優先している場合 (言い換えると、ブラウザのページリクエストの場合)、ページリクエストとして扱われます。それ以外の場合は `+server.js` で処理されます。 - `GET` リクエストに対するレスポンスには `Vary: Accept` ヘッダーが含まれるため、プロキシーやブラウザは HTML と JSON のレスポンスを別々にキャッシュします。 ## $types これまでの例を通してずっと、`$types.d.ts` ファイルからインポートしてきました。これは、TypeScript (または JavaScript を JSDoc の型アノテーションと) 使用している場合に最上位のファイル(root files)を扱う際に型の安全性をもたらすために SvelteKit が隠しディレクトリに作成するファイルです。 例えば、`let { data } = $props()` に `PageProps` (または `+layout.svelte` ファイルの場合は `LayoutProps`) アノテーションを付けると、`data` の型は `load` の戻り値であると TypeScript に伝えることができます: ```svelte ``` > [!NOTE] > The `PageProps` and `LayoutProps` types, added in 2.16.0, are a shortcut for typing the `data` prop as `PageData` or `LayoutData`, as well as other props, such as `form` for pages, or `children` for layouts. In earlier versions, you had to type these properties manually. For example, for a page: > > ```js > /// file: +page.svelte > /** @type {{ data: import('./$types').PageData, form: import('./$types').ActionData }} */ > let { data, form } = $props(); > ``` > > Or, for a layout: > > ```js > /// file: +layout.svelte > /** @type {{ data: import('./$types').LayoutData, children: Snippet }} */ > let { data, children } = $props(); > ``` `load` 関数に `PageLoad`、`PageServerLoad`、`LayoutLoad`、`LayoutServerLoad` (それぞれ `+page.js`、`+page.server.js`、`+layout.js`、`+layout.server.js`) というアノテーションを付けると、`params` と戻り値が正しく型付けされることが保証されます。 VS Code や、language server protocol と TypeScript plugin をサポートする IDE を使用している場合は、これらの型を _完全に_ 省略することができます! Svelte の IDE ツール類があなたのために正しい型を挿入してくれるので、あなたはご自身で型を書くことなく型チェックすることができます。これはコマンドラインツール `svelte-check` でも機能します。 `$types` の省略については、私たちの[ブログ記事](/blog/zero-config-type-safety)でより詳細な情報をお読み頂けます。 ## その他のファイル ルート(route)ディレクトリ内のその他のファイルは SvelteKit から無視されます。つまり、コンポーネントやユーティリティモジュールを、それらを必要とするルート(routes)に配置することができます。 コンポーネントやモジュールが複数のルート(routes)から必要な場合、[`$lib`]($lib) にそれらを配置すると良いでしょう。 ## その他の参考資料 - [Tutorial: Routing](/tutorial/kit/pages) - [Tutorial: API routes](/tutorial/kit/get-handlers) - [Docs: Advanced routing](advanced-routing) # Loading data [`+page.svelte`](routing#page-page.svelte) コンポーネント (と [`+layout.svelte`](routing#layout-layout.svelte) コンポーネント) をレンダリングする前に、データを取得する必要があるケースが多いでしょう。`load` 関数を定義することでこれができるようになります。 ## Page data `+page.svelte` ファイルは、`load` 関数をエクスポートする `+page.js` という兄弟ファイルを持つことができ、`load` 関数の戻り値は page で `data` プロパティを介して使用することができます。 ```js /// file: src/routes/blog/[slug]/+page.js /** @type {import('./$types').PageLoad} */ export function load({ params }) { return { post: { title: `Title for ${params.slug} goes here`, content: `Content for ${params.slug} goes here` } }; } ``` ```svelte

{data.post.title}

{@html data.post.content}
``` > [!LEGACY] > Before version 2.16.0, the props of a page and layout had to be typed individually: > ```js > /// file: +page.svelte > /** @type {{ data: import('./$types').PageData }} */ > let { data } = $props(); > ``` > > Svelte 4 では、代わりに `export let data` を使います。 生成される `$types` モジュールのおかげで、完全な型安全性を確保できます。 `+page.js` ファイルの `load` 関数はサーバーでもブラウザでも実行されます (ただし、`export const ssr = false` を設定した場合は[ブラウザでのみ実行されます](page-options#ssr))。`load` 関数を*常に*サーバーで実行させたければ (例えばプライベートな環境変数を使用していたり、データベースにアクセスする場合など)、代わりに `+page.server.js` に `load` 関数を置くとよいでしょう。 例えばブログ記事の `load` 関数をより現実的なものにするとしたら、以下のように、サーバー上でのみ実行され、データベースからデータを取得する、というものになるでしょう。 ```js /// file: src/routes/blog/[slug]/+page.server.js // @filename: ambient.d.ts declare module '$lib/server/database' { export function getPost(slug: string): Promise<{ title: string, content: string }> } // @filename: index.js // ---cut--- import * as db from '$lib/server/database'; /** @type {import('./$types').PageServerLoad} */ export async function load({ params }) { return { post: await db.getPost(params.slug) }; } ``` 型が `PageLoad` から `PageServerLoad` に変わっていることにご注意ください。server `load` 関数では追加の引数にアクセスすることができます。どのような場合に `+page.js` を使用し、どのような場合に `+page.server.js` を使用するのかを理解するには、[Universal vs server](load#Universal-vs-server) を参照してください。 ## Layout data `+layout.svelte` ファイルでも、`+layout.js` や `+layout.server.js` を通じてデータを読み込むことができます。 ```js /// file: src/routes/blog/[slug]/+layout.server.js // @filename: ambient.d.ts declare module '$lib/server/database' { export function getPostSummaries(): Promise> } // @filename: index.js // ---cut--- import * as db from '$lib/server/database'; /** @type {import('./$types').LayoutServerLoad} */ export async function load() { return { posts: await db.getPostSummaries() }; } ``` ```svelte
{@render children()}
``` > [!LEGACY] > `LayoutProps` was added in 2.16.0. In earlier versions, properties had to be typed individually: > ```js > /// file: +layout.svelte > /** @type {{ data: import('./$types').LayoutData, children: Snippet }} */ > let { data, children } = $props(); > ``` レイアウトの `load` 関数から返されたデータは、子の `+layout.svelte` コンポーネントやそのレイアウトに属する `+page.svelte` コンポーネントでも利用可能です。 ```svelte /// file: src/routes/blog/[slug]/+page.svelte

{data.post.title}

{@html data.post.content}
+++{#if next}

Next post: {next.title}

{/if}+++ ``` > [!NOTE] 複数の `load` 関数が同じキーを持つデータを返した場合、最後に返したものが'勝ちます'。レイアウトの `load` が `{ a: 1, b: 2 }` を返し、ページの `load` が `{ b: 3, c: 4 }` を返すと、結果は `{ a: 1, b: 3, c: 4 }` となります。 ## page.data `+page.svelte` コンポーネントとその上の各 `+layout.svelte` コンポーネントは、自身のデータとその親の全てのデータにアクセスすることができます。 場合によっては、その逆も必要かもしれません — 親レイアウトからページのデータや子レイアウトのデータにアクセスする必要があるかもしれません。例えば、最上位のレイアウト(root layout)から、`+page.js` や `+page.server.js` の `load` 関数から返された `title` プロパティにアクセスしたい場合があるでしょう。これは `page.data` で行うことができます: ```svelte {page.data.title} ``` `page.data` の型情報は `App.PageData` から提供されます。 > [!LEGACY] > `$app/state` was added in SvelteKit 2.12. If you're using an earlier version or are using Svelte 4, use `$app/stores` instead. > It provides a `page` store with the same interface that you can subscribe to, e.g. `$page.data.title`. ## Universal vs server これまで見てきたように、`load` 関数には2つの種類があります: * `+page.js` ファイルと `+layout.js` ファイルは、サーバーとブラウザの両方で実行される universal `load` 関数をエクスポートします * `+page.server.js` ファイルと `+layout.server.js` ファイルは、サーバーサイドでのみ実行される server `load` 関数をエクスポートします 概念上は同じものですが、気をつけなければならない重要な違いがあります。 ### いつ、どの load 関数が実行されるのか? server `load` 関数は _常に_ サーバーで実行されます。 デフォルトでは、universal `load` 関数は、ユーザーがはじめてページにアクセスしたときの SSR 中にサーバーで実行されます。そのあとのハイドレーション中に、[fetch リクエスト](#Making-fetch-requests) で取得したレスポンスを再利用してもう一度実行されます。それ以降の universal `load` 関数の呼び出しは、すべてブラウザで行われます。この動作は [page options](page-options) によってカスタマイズすることができます。[サーバーサイドレンダリング](page-options#ssr) を無効にした場合、SPA となるため、universal `load` 関数は _常に_ クライアントで実行されるようになります。 ルート(route)に universal と server の両方の `load` 関数が含まれている場合、server `load` が最初に実谷されます。 `load` 関数は実行時に呼び出されます。ページを [プリレンダリング](page-options#prerender) する場合は、ビルド時に呼び出されます。 ### Input universal `load` 関数と server `load` 関数はどちらも、リクエストを表すプロパティ (`params`、`route`、`url`) と様々な関数 (`fetch`、`setHeaders`、`parent`、`depends`、`untrack`) にアクセスできます。これらについては、以下のセクションで説明します。 server `load` 関数は `ServerLoadEvent` を引数にとって呼び出されます。`ServerLoadEvent` は、`RequestEvent` から `clientAddress`、`cookies`、`locals`、`platform`、`request` を継承しています。 universal `load` 関数は、`LoadEvent` を引数にとって呼び出されます。`LoadEvent` は `data` プロパティを持っています。もし `+page.js` と `+page.server.js` (または `+layout.js` と `+layout.server.js`) の両方に `load` 関数がある場合、server `load` 関数の戻り値が、universal `load` 関数の引数の `data` プロパティとなります。 ### Output universal `load` 関数は、任意の値(カスタムクラスやコンポーネントコンストラクタなどを含む)を含むオブジェクトを返すことができます。 server `load` 関数は、ネットワークで転送できるようにするために、[devalue](https://github.com/rich-harris/devalue) でシリアライズできるデータ (つまり JSON で表現できるものに加え、`BigInt`、`Date`、`Map`、`Set`、`RegExp` や、繰り返し/循環参照など) を返さなければなりません。データには [promises](#Streaming-with-promises) を含めることができ、その場合はブラウザにストリーミングされます。 ### どちらを使用すべきか server `load` 関数は、データベースやファイルシステムからデータを直接アクセスする必要がある場合や、プライベートな環境変数を使用する必要がある場合に有用です。 universal `load` 関数は、外部の API から データを `fetch` (取得) する必要があり、プライベートなクレデンシャルが必要ない場合に便利です。なぜなら、SvelteKit はあなたのサーバーを経由せずに、その API から直接データを取得することができるからです。また、Svelte コンポーネントコンストラクタのような、シリアライズできないものを返す必要がある場合にも便利です。 まれに、両方を同時に使用する必要がある場合もあります。例えば、サーバーからのデータで初期化されたカスタムクラスのインスタンスを返す必要がある場合です。両方を使用する場合は、server `load` の戻り値は直接ページには渡されず、universal `load` 関数に (`data` プロパティとして) 渡されます: ```js /// file: src/routes/+page.server.js /** @type {import('./$types').PageServerLoad} */ export async function load() { return { serverMessage: 'hello from server load function' }; } ``` ```js /// file: src/routes/+page.js // @errors: 18047 /** @type {import('./$types').PageLoad} */ export async function load({ data }) { return { serverMessage: data.serverMessage, universalMessage: 'hello from universal load function' }; } ``` ## URL data を使用する 多くの場合、`load` 関数は何らかの形で URL に依存します。そのため、`load` 関数では `url`、`route`、`params` を提供しています。 ### url [`URL`](https://developer.mozilla.org/ja/docs/Web/API/URL) のインスタンスで、`origin`、`hostname`、`pathname`、`searchParams` ([`URLSearchParams`](https://developer.mozilla.org/ja/docs/Web/API/URLSearchParams) オブジェクトとしてパースされたクエリ文字列を含む) を含んでいます。`url.hash` はサーバーで利用できないため、`load` 中にアクセスすることはできません。 > [!NOTE] 環境によっては、サーバーサイドレンダリング時のリクエストヘッダからこれが導出されることもあります。例えば [adapter-node](adapter-node) では、URL を正しく設定するために adapter の設定をする必要があるかもしれません。 ### route 現在のルート(route)ディレクトリの名前を含んでいます。`src/routes` との相対です: ```js /// file: src/routes/a/[b]/[...c]/+page.js /** @type {import('./$types').PageLoad} */ export function load({ route }) { console.log(route.id); // '/a/[b]/[...c]' } ``` ### params `params` は `url.pathname` と `route.id` から導出されます。 `route.id` が `/a/[b]/[...c]` で、`url.pathname` が `/a/x/y/z` の場合、`params` オブジェクトはこのようになります: ```json { "b": "x", "c": "y/z" } ``` ## fetch リクエストの作成 外部の API や `+server.js` ハンドラからデータを取得するには、提供されている `fetch` 関数を使用します。これは [ネイティブの `fetch` web API](https://developer.mozilla.org/ja/docs/Web/API/fetch) と同じように動作しますが、いくつか追加の機能があります: - ページリクエストの `cookie` と `authorization` ヘッダーを継承するので、サーバー上でクレデンシャル付きのリクエストを行うことができます - サーバー上で、相対パスのリクエストを行うことができます (通常、`fetch` はサーバーのコンテキストで使用する場合にはオリジン付きの URL が必要です) - サーバーで動作している場合、内部リクエスト (例えば `+server.js` ルート(routes)に対するリクエスト) は直接ハンドラ関数を呼び出すので、HTTP を呼び出すオーバーヘッドがありません - サーバーサイドレンダリング中は、`Response` オブジェクトの `text` メソッドと `json` メソッドと `arrayBuffer` メソッドにフックすることにより、レスポンスはキャプチャされ、レンダリング済の HTML にインライン化されます。ヘッダーは、[`filterSerializedResponseHeaders`](hooks#Server-hooks-handle) で明示的に指定されない限り、シリアライズされないことにご注意ください。 - ハイドレーション中は、レスポンスは HTML から読み込まれるため、一貫性が保証され、追加のネットワークリクエストを防ぎます。もし、`load` 関数の `fetch` ではなくブラウザの `fetch` を使用しているときにブラウザコンソールに警告が出た場合は、これが理由です。 ```js /// file: src/routes/items/[id]/+page.js /** @type {import('./$types').PageLoad} */ export async function load({ fetch, params }) { const res = await fetch(`/api/items/${params.id}`); const item = await res.json(); return { item }; } ``` ## Cookies server `load` 関数では [`cookies`](@sveltejs-kit#Cookies) を取得したり設定したりすることができます。 ```js /// file: src/routes/+layout.server.js // @filename: ambient.d.ts declare module '$lib/server/database' { export function getUser(sessionid: string | undefined): Promise<{ name: string, avatar: string }> rerunning-load-functions} // @filename: index.js // ---cut--- import * as db from '$lib/server/database'; /** @type {import('./$types').LayoutServerLoad} */ export async function load({ cookies }) { const sessionid = cookies.get('sessionid'); return { user: await db.getUser(sessionid) }; } ``` Cookie は、ターゲットホストが Sveltekit アプリケーションと同じか、より明確・詳細(specific)なサブドメインである場合にのみ、SvelteKit から提供される `fetch` 関数を介して引き渡されます。 例えば、SvelteKit が my.domain.com でサーブしている場合: - domain.com は cookie を受け取りません - my.domain.com は cookie を受け取ります - api.domain.com は cookie を受け取りません - sub.my.domain.com は cookie を受け取ります その他の cookie は、`credentials: 'include'` がセットされているときには渡されません。なぜなら、Sveltekit はどの cookie がどのドメインに属しているかわからないからです (ブラウザはこの情報を渡さないからです)。そのため、cookie を転送するのは安全ではありません。これを回避するには、[handleFetch hook](hooks#Server-hooks-handleFetch) をお使いください。 ## Headers server `load` 関数と universal `load` 関数はどちらも `setHeaders` 関数にアクセスでき、サーバー上で実行している場合、 レスポンスにヘッダーを設定できます (ブラウザで実行している場合、`setHeaders` には何の効果もありません)。これは、ページをキャッシュさせる場合に便利です、例えば: ```js // @errors: 2322 1360 /// file: src/routes/products/+page.js /** @type {import('./$types').PageLoad} */ export async function load({ fetch, setHeaders }) { const url = `https://cms.example.com/products.json`; const response = await fetch(url); // Headers are only set during SSR, caching the page's HTML // for the same length of time as the underlying data. setHeaders({ age: response.headers.get('age'), 'cache-control': response.headers.get('cache-control') }); return response.json(); } ``` 同じヘッダーを複数回設定することは (たとえ別々の `load` 関数であっても) エラーとなります。指定したヘッダーを設定できるのは一度だけです。`set-cookie` ヘッダーは、`setHeaders` では追加できません。代わりに、`cookies.set(name, value, options)` を使用してください。 ## Using parent data `load` 関数が親の `load` 関数からのデータにアクセスできたら便利なことがあるでしょう。`await parent()` でこれを行うことができます: ```js /// file: src/routes/+layout.js /** @type {import('./$types').LayoutLoad} */ export function load() { return { a: 1 }; } ``` ```js /// file: src/routes/abc/+layout.js /** @type {import('./$types').LayoutLoad} */ export async function load({ parent }) { const { a } = await parent(); return { b: a + 1 }; } ``` ```js /// file: src/routes/abc/+page.js /** @type {import('./$types').PageLoad} */ export async function load({ parent }) { const { a, b } = await parent(); return { c: a + b }; } ``` ```svelte

{data.a} + {data.b} = {data.c}

``` > [!NOTE] `+page.js` の `load` 関数が、直接の親だけなく、両方のレイアウトの `load` 関数からマージされたデータを受け取っていることにご注意ください。 `+page.server.js` と `+layout.server.js` の中では、`parent` は親の `+layout.server.js` ファイルからデータを取得します。 `+page.js` や `+layout.js` では、親の `+layout.js` ファイルからデータを返します。しかし、`+layout.js` が見つからない場合は `({ data }) => data` 関数として扱われます。つまり、`+layout.js` ファイルによって'シャドウ(shadowed)'されていない親の `+layout.server.js` ファイルからデータを返します。 `await parent()` を使用する際はウォータフォールを発生させないよう注意してください。例えば、こちらの `getData(params)` は `parent()` の呼び出しの結果には依存しないので、レンダリングの遅延を避けるために最初に呼び出すほうが良いでしょう。 ```js /// file: +page.js // @filename: ambient.d.ts declare function getData(params: Record): Promise<{ meta: any }> // @filename: index.js // ---cut--- /** @type {import('./$types').PageLoad} */ export async function load({ params, parent }) { ---const parentData = await parent();--- const data = await getData(params); +++const parentData = await parent();+++ return { ...data, meta: { ...parentData.meta, ...data.meta } }; } ``` ## Errors `load` 中にエラーがスローされた場合、最も近くにある [`+error.svelte`](routing#error) がレンダリングされます。[想定されるエラー](errors#Expected-errors)には、`@sveltejs/kit` からインポートできる `error` ヘルパーを使用して HTTP ステータスコードとオプションのメッセージを指定できます: ```js /// file: src/routes/admin/+layout.server.js // @filename: ambient.d.ts declare namespace App { interface Locals { user?: { name: string; isAdmin: boolean; } } } // @filename: index.js // ---cut--- import { error } from '@sveltejs/kit'; /** @type {import('./$types').LayoutServerLoad} */ export function load({ locals }) { if (!locals.user) { error(401, 'not logged in'); } if (!locals.user.isAdmin) { error(403, 'not an admin'); } } ``` `error(...)` を呼び出すと例外がスローされるため、ヘルパー関数の内側から簡単に実行を止めることができます。 [予期せぬエラー](errors#Unexpected-errors)がスローされた場合、SvelteKit は [`handleError`](hooks#Shared-hooks-handleError) を呼び出し、それを 500 Internal Error として処理します。 > [!NOTE] [SvelteKit 1.x 系では](migrating-to-sveltekit-2#redirect-and-error-are-no-longer-thrown-by-you)、ご自分で error を `throw` する必要がありました ## Redirects ユーザーをリダイレクトさせるには、`@sveltejs/kit` からインポートできる `redirect` ヘルパーを使用して、ステータスコード `3xx` と一緒にリダイレクト先の location を指定します。`error(...)` と同じように、`redirect(...)` を呼び出すと例外がスローされるため、ヘルパー関数の内側から簡単に実行を止めることができます。 ```js /// file: src/routes/user/+layout.server.js // @filename: ambient.d.ts declare namespace App { interface Locals { user?: { name: string; } } } // @filename: index.js // ---cut--- import { redirect } from '@sveltejs/kit'; /** @type {import('./$types').LayoutServerLoad} */ export function load({ locals }) { if (!locals.user) { redirect(307, '/login'); } } ``` > [!NOTE] `try {...}` ブロックの中で `redirect()` を使用してはいけません。redirect がすぐにその catch ステートメントをトリガーしてしまうからです。 ブラウザでは、[`$app.navigation`]($app-navigation) からインポートできる [`goto`]($app-navigation#goto) を使うことで、`load` 関数の外側でプログラム的にナビゲーションを行うことができます。 > [!NOTE] [SvelteKit 1.x 系では](migrating-to-sveltekit-2#redirect-and-error-are-no-longer-thrown-by-you)、ご自分で `redirect` を `throw` する必要がありました ## Streaming with promises server `load` を使用する場合、promise が解決されると同時にブラウザにストリームされます。これにより、遅くて重要でないデータがある場合でも、すべてのデータが利用可能になる前にページのレンダリングを開始することができます: ```js /// file: src/routes/blog/[slug]/+page.server.js // @filename: ambient.d.ts declare global { const loadPost: (slug: string) => Promise<{ title: string, content: string }>; const loadComments: (slug: string) => Promise<{ content: string }>; } export {}; // @filename: index.js // ---cut--- /** @type {import('./$types').PageServerLoad} */ export async function load({ params }) { return { // make sure the `await` happens at the end, otherwise we // can't start loading comments until we've loaded the post comments: loadComments(params.slug), post: await loadPost(params.slug) }; } ``` これはロード状態(loading states)のスケルトンを作成するのに便利です、例えば: ```svelte

{data.post.title}

{@html data.post.content}
{#await data.comments} Loading comments... {:then comments} {#each comments as comment}

{comment.content}

{/each} {:catch error}

error loading comments: {error.message}

{/await} ``` データをストリーミングする場合は、promise の reject を正しく処理するようにしてください。具体的には、レンダリングの開始時点 (本来この時点で catch される) より前に遅延ロード (lazy-loaded) された promise が失敗し、エラーを処理していない場合、server が "unhandled promise rejection" エラーでクラッシュする可能性があります。SvelteKit の `fetch` を `load` 関数で直接使用する場合は SvelteKit がこのケースを処理してくれます。それ以外の promise の場合は、何もしない `catch` (noop-`catch`) をアタッチし、処理済であることを明示するだけで十分です。 ```js /// file: src/routes/+page.server.js /** @type {import('./$types').PageServerLoad} */ export function load({ fetch }) { const ok_manual = Promise.reject(); ok_manual.catch(() => {}); return { ok_manual, ok_fetch: fetch('/fetch/that/could/fail'), dangerous_unhandled: Promise.reject() }; } ``` > [!NOTE] AWS Lambda や Firebase のような ストリーミングをサポートしないプラットフォームでは、レスポンスはバッファされます。つまり、すべての promise が解決してからでないとページがレンダリングされないということです。もしプロキシ (例えば NGINX) を使用している場合は、プロキシされたサーバーからのレスポンスをバッファしないようにしてください。 > [!NOTE] ストリーミングデータは JavaScript が有効なときにのみ動作します。ページがサーバーでレンダリングされる場合、universal `load` 関数からは promise を返すのは避けたほうがよいでしょう、ストリーミングされないからです (代わりに、関数がブラウザで再実行されるときに promise が再作成されます)。 > [!NOTE] レスポンスのヘッダーとステータスコードは、レスポンスがストリーミングを開始すると変更することはできなくなります。そのため、ストリームされた promise の中で `setHeaders` を呼んだりリダイレクトをスローしたりすることはできません。 > [!NOTE] [SvelteKit 1.x 系では](migrating-to-sveltekit-2#Top-level-promises-are-no-longer-awaited)、トップレベルの promise は自動的に await され、ネストした promise だけがストリーミングされていました。 ## Parallel loading ページをレンダリング (またはページにナビゲーション) するとき、SvelteKit はすべての `load` 関数を同時に実行し、リクエストのウォーターフォールを回避します。クライアントサイドナビゲーションのときは、複数の server `load` 関数の呼び出し結果が単一のレスポンスにグループ化されます。すべての `load` 関数が返されると、ページがレンダリングされます。 ## load 関数の再実行 SvelteKit は それぞれの `load` 関数の依存関係を追跡し、ナビゲーションの際に不必要に再実行されるのを回避します。 例えば、このような `load` 関数のペアがあるとして… ```js /// file: src/routes/blog/[slug]/+page.server.js // @filename: ambient.d.ts declare module '$lib/server/database' { export function getPost(slug: string): Promise<{ title: string, content: string }> } // @filename: index.js // ---cut--- import * as db from '$lib/server/database'; /** @type {import('./$types').PageServerLoad} */ export async function load({ params }) { return { post: await db.getPost(params.slug) }; } ``` ```js /// file: src/routes/blog/[slug]/+layout.server.js // @filename: ambient.d.ts declare module '$lib/server/database' { export function getPostSummaries(): Promise> } // @filename: index.js // ---cut--- import * as db from '$lib/server/database'; /** @type {import('./$types').LayoutServerLoad} */ export async function load() { return { posts: await db.getPostSummaries() }; } ``` …もし、`/blog/trying-the-raw-meat-diet` から `/blog/i-regret-my-choices` に移動したら、`params.slug` が変更されているので、`+page.server.js` のほうは再実行されるでしょう。`+layout.server.js` のほうは再実行されません、なぜならデータがまだ有効だからです。言い換えると、`db.getPostSummaries()` を2回呼び出すことはないということです。 `await parent()` を呼び出している `load` 関数は、親の `load` 関数が再実行された場合に再実行されます。 依存関係の追跡は、`load` 関数から返したあとには適用されません。例えば、ネストした [promise](#Streaming-with-promises) の内側で `params.x` にアクセスしている場合、`params.x` が変更されても関数の再実行は行われません (ご心配なく、誤ってこれを行っても開発中に警告が表示されます)。代わりに、`load` 関数の本体部分でパラメータにアクセスしてください。 search parameter は url の他の部分とは独立し追跡されます。例えば、`load` 関数の中で `event.url.searchParams.get("x")` にアクセスすると、`?x=1` から `?x=2` にナビゲーションするときにその `load` 関数が再実行されますが、`?x=1&y=1` から `?x=1&y=2` にナビゲーションするときには再実行されません。 ### Untracking dependencies まれに、依存関係の追跡メカニズムからなにかを除外したいケースがあります。このような場合、`untrack` 関数を使用します: ```js /// file: 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!' }; } } ``` ### Manual invalidation 現在のページに適用される `load` 関数は、[`invalidate(url)`]($app-navigation#invalidate) を使用することで再実行させることもできます。これは `url` に依存しているすべての `load` 関数を再実行させるものです。[`invalidateAll()`]($app-navigation#invalidateAll) は、すべての `load` 関数を再実行させます。server load 関数の場合は、クライアントに秘情報が漏れるのを防ぐため、取得した `url` に自動的に依存することはありません。 `load` 関数が `fetch(url)` や `depends(url)` を呼び出している場合、その `load` 関数は `url` に依存しています。`url` には `[a-z]:` から始まるカスタムの識別子を指定することができることにご注意ください: ```js /// file: 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() }; } ``` ```svelte

random number: {data.number}

``` ### load 関数はいつ再実行されるのか まとめると、`load` 関数は以下のシチュエーションで再実行されます: - `params` のプロパティを参照していて、その値が変更された場合 - `url` のプロパティ (例えば `url.pathname` や `url.search`) を参照していて、その値が変更された場合。`request.url` のプロパティは追跡されません - `url.searchParams.get(...)` や `url.searchParams.getAll(...)` や `url.searchParams.has(...)` を呼び出しており、その該当するパラメータが変更された場合。`url.searchParams` の他のプロパティにアクセスした場合は、`url.search` にアクセスした場合と同じ効果になる。 - `await parent()` を呼び出していて、親の `load` 関数が再実行されたとき - [`fetch`](#Making-fetch-requests) (universal load のみ) や [`depends`](@sveltejs-kit#LoadEvent) を介して特定の URL に対する依存を宣言していて、その URL が [`invalidate(url)`]($app-navigation#invalidate) で無効(invalid)であるとマークされた場合 - [`invalidateAll()`]($app-navigation#invalidateAll) によって全ての有効な `load` 関数が強制的に再実行された場合 `params` と `url` は、`` リンクのクリックや、[`` のやりとり](form-actions#GET-vs-POST)、[`goto`]($app-navigation#goto) の呼び出し、[`redirect`](@sveltejs-kit#redirect) に応じて変更されます。 `load` 関数の再実行は、対応する `+layout.svelte` や `+page.svelte` 内の `data` プロパティが更新されるだけで、コンポーネントは再作成されることはありません。結果として、内部の状態は保持されます。もし、この挙動がお望みでなければ、[`afterNavigate`]($app-navigation#afterNavigate) コールバック内でリセットしたり、コンポーネントを [`{#key ...}`](https://svelte.jp/docs#template-syntax-key) ブロックでラップしたりしてリセットすることができます。 ## 認証に対する影響 データ読み込みに関するいくつかの機能は、認証チェックに重要な影響があります: - レイアウトの `load` 関数は、例えばその子ルート間のクライアントサイドナビゲーションなど、すべてのリクエストで実行されるわけではありません [(load 関数はいつ再実行されるのか)](load#Rerunning-load-functions-When-do-load-functions-rerun) - レイアウトとページの `load` 関数は、`await parent()` を呼び出していない限り同時に実行されます。もしレイアウトの `load` がスローした場合、ページの `load` 関数は実行されますが、クライアントは戻り値のデータを受け取れません。 保護されたコードの前に認証チェックが行われるようにするには、いくつかの戦略が考えられます。 データのウォータフォールを防ぎつつ、レイアウトの `load` のキャッシュを保持するには: - [hooks](hooks) を使用し、`load` 関数が実行されるよりも前に複数のルートを保護する - ルート固有の保護が必要な場合は `+page.server.js` の `load` 関数で直接認証ガード (auth guard) を使用する 認証ガード (auth guard) を `+layout.server.js` に置くと、全ての子ページで保護されたコードの前で `await parent()` を呼び出す必要があります。全ての子ページが `await parent()` の戻り値のデータに依存していないのであれば、他のオプションのほうがパフォーマンスが良いでしょう。 ## その他の参考資料 - [Tutorial: Loading data](/tutorial/kit/page-data) - [Tutorial: Errors and redirects](/tutorial/kit/error-basics) - [Tutorial: Advanced loading](/tutorial/kit/await-parent) # Form actions `+page.server.js` ファイルは _actions_ をエクスポートできます。これによって、`` 要素を使用することでサーバーにデータを `POST` することができます。 `` を使用する場合、クライアントサイドの JavaScript はオプションですが、JavaScript によって form のインタラクションを簡単にプログレッシブに強化(_progressively enhance_)することができ、最高のユーザーエクスペリエンスを提供することができます。 ## Default actions 最もシンプルなケースでは、ページは `default` の action を宣言します: ```js /// file: src/routes/login/+page.server.js /** @type {import('./$types').Actions} */ export const actions = { default: async (event) => { // TODO log the user in } }; ``` `/login` ページからこの action を呼び出すには、`` を追加します。JavaScript は必要ありません: ```svelte ``` もし誰かがボタンをクリックしたら、ブラウザは form のデータを `POST` リクエストでサーバーに送信し、デフォルトの action が実行されます。 > [!NOTE] Action は常に `POST` リクエストを使用します。`GET` リクエストには決して副作用があってはならないからです。 また、`action` 属性を追加し、リクエスト先のページを指し示すことで、他のページから action を呼び出すこともできます (例えば、最上位のレイアウト(root layout)にある nav にログイン用の widget がある場合): ```html /// file: src/routes/+layout.svelte
``` ## Named actions 単一の `default` の action の代わりに、名前付きの action (named action) を必要なだけ持つことができます: ```js /// file: src/routes/login/+page.server.js /** @satisfies {import('./$types').Actions} */ export const actions = { --- default: async (event) => {--- +++ login: async (event) => {+++ // TODO log the user in }, +++ register: async (event) => { // TODO register the user }+++ }; ``` 名前付きの action (named action) を呼び出すには、クエリパラメータに `/` を接頭辞に付与したその action の名前を追加します: ```svelte
``` ```svelte ``` `action` 属性と同じように、button の `formaction` 属性を使用することができ、こうすると親の `` とは別の action に同じ form のデータを `POST` することができます: ```svelte /// file: src/routes/login/+page.svelte ++++++
``` > [!NOTE] 名前付き action (named action) の隣にデフォルトの action を置くことはできません。なぜなら リダイレクト無しで名前付き action (named action) に POST をすると、クエリパラメータが URL に保持され、それ以降デフォルトの POST をしようとしても以前 POST した名前付き action (named action) を通ってしまうからです。 ## action の構造 action はそれぞれ `RequestEvent` オブジェクトを受け取って、`request.formData()` でデータを読み込むことができます。リクエスト (例えば、cookie をセットしてユーザーをログインさせるなど) を処理したあと、action は次の更新まで、対応するページでは `form` プロパティで、アプリ全体では `page.form` で利用可能なデータで応答することができます。 ```js /// file: src/routes/login/+page.server.js // @filename: ambient.d.ts declare module '$lib/server/db'; // @filename: index.js // ---cut--- import * as db from '$lib/server/db'; /** @type {import('./$types').PageServerLoad} */ export async function load({ cookies }) { const user = await db.getUserFromSession(cookies.get('sessionid')); return { user }; } /** @satisfies {import('./$types').Actions} */ export const actions = { login: async ({ cookies, request }) => { const data = await request.formData(); const email = data.get('email'); const password = data.get('password'); const user = await db.getUser(email); cookies.set('sessionid', await db.createSession(user), { path: '/' }); return { success: true }; }, register: async (event) => { // TODO register the user } }; ``` ```svelte {#if form?.success}

Successfully logged in! Welcome back, {data.user.name}

{/if} ``` > [!LEGACY] > `PageProps` was added in 2.16.0. In earlier versions, you had to type the `data` and `form` properties individually: > ```js > /// file: +page.svelte > /** @type {{ data: import('./$types').PageData, form: import('./$types').ActionData }} */ > let { data, form } = $props(); > ``` > > In Svelte 4, you'd use `export let data` and `export let form` instead to declare properties. ### Validation errors 無効なデータが原因でリクエストが処理できなかった場合、再試行できるようにするために、直前に送信した form の値とともに validation error をユーザーに返すことができます。`fail` 関数は、HTTP ステータスコード (通常、validation error の場合は 400 か 422) をデータとともに返します。ステータスコードは `page.status` から使用することができ、data は `form` から使用することができます: ```js /// file: src/routes/login/+page.server.js // @filename: ambient.d.ts declare module '$lib/server/db'; // @filename: index.js // ---cut--- +++import { fail } from '@sveltejs/kit';+++ import * as db from '$lib/server/db'; /** @satisfies {import('./$types').Actions} */ export const actions = { login: async ({ cookies, request }) => { const data = await request.formData(); const email = data.get('email'); const password = data.get('password'); +++ if (!email) { return fail(400, { email, missing: true }); }+++ const user = await db.getUser(email); +++ if (!user || user.password !== db.hash(password)) { return fail(400, { email, incorrect: true }); }+++ cookies.set('sessionid', await db.createSession(user), { path: '/' }); return { success: true }; }, register: async (event) => { // TODO register the user } }; ``` > [!NOTE] 念のため、password は返さず、email のみをページに返していることにご注意ください。 ```svelte /// file: src/routes/login/+page.svelte
+++ {#if form?.missing}

The email field is required

{/if} {#if form?.incorrect}

Invalid credentials!

{/if}+++
``` 戻り値は JSON としてシリアライズ可能でなければなりません。その上で、構造は完全にあなた次第です。例えば、もしページに複数の form がある場合、返された `form` データがどの `
` を参照しているかを `id` プロパティなどで区別することができます。 ### Redirects redirect (と error) は [`load`](load#Redirects) のそれと同じように機能します: ```js // @errors: 2345 /// file: src/routes/login/+page.server.js // @filename: ambient.d.ts declare module '$lib/server/db'; // @filename: index.js // ---cut--- import { fail, +++redirect+++ } from '@sveltejs/kit'; import * as db from '$lib/server/db'; /** @satisfies {import('./$types').Actions} */ export const actions = { login: async ({ cookies, request, +++url+++ }) => { const data = await request.formData(); const email = data.get('email'); const password = data.get('password'); const user = await db.getUser(email); if (!user) { return fail(400, { email, missing: true }); } if (user.password !== db.hash(password)) { return fail(400, { email, incorrect: true }); } cookies.set('sessionid', await db.createSession(user), { path: '/' }); +++ if (url.searchParams.has('redirectTo')) { redirect(303, url.searchParams.get('redirectTo')); }+++ return { success: true }; }, register: async (event) => { // TODO register the user } }; ``` ## Loading data action の実行後、そのページは (リダイレクトや予期せぬエラーが発生しない限り) 再レンダリングされ、action の戻り値が `form` プロパティとしてそのページで使用できるようになります。つまり、ページの `load` 関数は、action が完了したあとに実行されるということです。 `handle` は action が呼び出される前に実行され、`load` 関数より前に再実行されることはないことに注意してください。つまり、例えば `handle` を使用して cookie を元に `event.locals` に値を入れる場合、action で cookie を設定したり削除したりするときは `event.locals` を更新しなければなりません: ```js /// file: src/hooks.server.js // @filename: ambient.d.ts declare namespace App { interface Locals { user: { name: string; } | null } } // @filename: global.d.ts declare global { function getUser(sessionid: string | undefined): { name: string; }; } export {}; // @filename: index.js // ---cut--- /** @type {import('@sveltejs/kit').Handle} */ export async function handle({ event, resolve }) { event.locals.user = await getUser(event.cookies.get('sessionid')); return resolve(event); } ``` ```js /// file: src/routes/account/+page.server.js // @filename: ambient.d.ts declare namespace App { interface Locals { user: { name: string; } | null } } // @filename: index.js // ---cut--- /** @type {import('./$types').PageServerLoad} */ export function load(event) { return { user: event.locals.user }; } /** @satisfies {import('./$types').Actions} */ export const actions = { logout: async (event) => { event.cookies.delete('sessionid', { path: '/' }); event.locals.user = null; } }; ``` ## Progressive enhancement 前のセクションでは [クライアントサイドの JavaScriptなしで動作する](https://kryogenix.org/code/browser/everyonehasjs.html) `/login` action を構築しました — `fetch` は見当たりません。これは素晴らしいことですが、JavaScript が利用可能な場合は、より良いユーザーエクスペリンスを提供するために form のインタラクションをプログレッシブに強化 (progressively enhance) することができます。 ### use:enhance form をプログレッシブに強化する最も簡単な方法は、`use:enhance` action を追加することです: ```svelte /// file: src/routes/login/+page.svelte ``` > [!NOTE] `use:enhance` can only be used with forms that have `method="POST"`. It will not work with `method="GET"`, which is the default for forms without a specified method. Attempting to use `use:enhance` on forms without `method="POST"` will result in an error. > [!NOTE] ええ、`enhance` action と `` をどちらも 'action' と呼んでいて、少し紛らわしいですよね。このドキュメントは action でいっぱいです。申し訳ありません。 引数が無い場合、`use:enhance` は、ブラウザネイティブの動作を、フルページリロードを除いてエミュレートします。それは: - action が送信元のページと同じ場所にある場合に限り、成功レスポンスまたは不正なレスポンスに応じて、`form` プロパティと `page.form` と `page.status` を更新します。例えば、`` というようなフォームの場合、`form` プロパティと `page.form` state は更新されません。これは、ネイティブのフォーム送信では action があるページにリダイレクトされるからです。どちらにしても更新させたい場合は、[`applyAction`](#Progressive-enhancement-Customising-use:enhance) を使用してください - `` 要素をリセットします - 成功レスポンスの場合は `invalidateAll` で全てのデータを無効化・最新化(invalidate)します - リダイレクトレスポンスの場合は `goto` を呼び出します - エラーが発生した場合はもっとも近くにある `+error` 境界をレンダリングします - 適切な要素に [フォーカスをリセット](accessibility#Focus-management) します ### use:enhance をカスタマイズする この挙動をカスタマイズするために、form が送信される直前に実行される `SubmitFunction` 関数を提供することができます。そして (オプションで) `ActionResult` を引数に取るコールバックを返すことができます。もしコールバックを返す場合、上述のデフォルトの動作はトリガーされません。元に戻すには、`update` を呼び出してください。 ```svelte { // `formElement` はこの `` 要素です // `formData` は送信される予定の `FormData` オブジェクトです // `action` はフォームが POST される URL です // `cancel()` を呼び出すと送信(submission)を中止します // `submitter` は、フォームの送信を実行した `HTMLElement` です return async ({ result, update }) => { // `result` は `ActionResult` オブジェクトです // `update` は、このコールバックが設定されていない場合に起動されるデフォルトのロジックを起動する関数です }; }} > ``` これらの関数を、ロード中の UI (loading UI) を表示したり隠したりすることなどに使用できます。 コールバックを返す場合は `use:enhance` のデフォルトの動作の一部を再現させる必要があるかもしれませんが、成功レスポンスで全てのデータを無効化・最新化(invalidate)する必要はありません。このような場合には `applyAction` を使用するとよいでしょう: ```svelte /// file: src/routes/login/+page.svelte { return async ({ result }) => { // `result` は `ActionResult` オブジェクトです +++ if (result.type === 'redirect') { goto(result.location); } else { await applyAction(result); }+++ }; }} > ``` `applyAction(result)` の挙動は `result.type` に依存しています: - `success`, `failure` — `page.status` を `result.status` に設定し、`form` と `page.form` を `result.data` で更新します (`enhance` の `update` とは対照的に、送信元がどこかは関係ありません) - `redirect` — `goto(result.location, { invalidateAll: true })` を呼び出します - `error` — もっとも近くにある `+error` 境界を `result.error` でレンダリングします いずれの場合でも、[フォーカスはリセットされます](accessibility#Focus-management)。 ### Custom event listener `use:enhance` ではなく、`` の通常のイベントリスナーを使うことで、ご自身でプログレッシブ・エンハンスメント(progressive enhancement)を実装することもできます: ```svelte
``` 処理を進める前に、`$app/forms` の `deserialize` でレスポンスをデシリアライズする必要があることにご注意ください。`JSON.parse()` では不十分です。なぜなら、例えば `load` 関数のような form action は、`Date` や `BigInt` オブジェクトも戻り値としてサポートしているからです。 もし `+page.server.js` と `+server.js` のどちらも存在する場合、デフォルトでは、`fetch` リクエストは `+server.js` のほうにルーティングされます。`+page.server.js` の action に `POST` をするには、カスタムの `x-sveltekit-action` ヘッダーを使用します: ```js const response = await fetch(this.action, { method: 'POST', body: data, +++ headers: { 'x-sveltekit-action': 'true' }+++ }); ``` ## Alternatives サーバーにデータを送信する方法として、プログレッシブな強化(progressively enhance)を行うことができるため Form actions は望ましい方法ですが、[`+server.js`](routing#server) ファイルを使用して (例えば) JSON API を公開することもできます。それは例えばこのように行います: ```svelte ``` ```js // @errors: 2355 1360 2322 /// file: src/routes/api/ci/+server.js /** @type {import('./$types').RequestHandler} */ export function POST() { // do something } ``` ## GET vs POST これまで見てきたように、フォームアクションを使うには、`method="POST"` を使用する必要があります。 サーバーにデータを `POST` する必要がないフォームもあるでしょう — 例えば検索入力(search inputs)です。これに対応するには `method="GET"` (または、`method` を全く書かないのも同等です) を使うことができ、そして SvelteKit はそれを `
` 要素のように扱い、フルページナビゲーションの代わりにクライアントサイドルーターを使用します。: ```html
``` この form を送信すると `/search?q=...` に移動して load 関数が実行されますが、action は実行されません。`
` 要素と同じように、[`data-sveltekit-reload`](link-options#data-sveltekit-reload) 属性、 [`data-sveltekit-replacestate`](link-options#data-sveltekit-replacestate) 属性、[`data-sveltekit-keepfocus`](link-options#data-sveltekit-keepfocus) 属性、 [`data-sveltekit-noscroll`](link-options#data-sveltekit-noscroll) 属性を `
` に設定することができ、ルーターの挙動をコントロールすることができます。 ## その他の参考資料 - [Tutorial: Forms](/tutorial/kit/the-form-element) # Page options デフォルトでは、SvelteKit はどのコンポーネントも最初はサーバーでレンダリング (または [プリレンダリング](glossary#Prerendering)) し、それを HTML としてクライアントに送信します。その後、ブラウザ上でコンポーネントを再度レンダリングし、[**ハイドレーション(hydration)**](glossary#Hydration)と呼ばれるプロセスでそれをインタラクティブなものにします。このため、コンポーネントが両方の場所で実行できることを確認する必要があります。SvelteKit はそれから [**ルーター(router)**](routing) を初期化し、その後のナビゲーションを引き継ぎます。 これらはそれぞれオプションを [`+page.js`](routing#page-page.js) や [`+page.server.js`](routing#page-page.server.js) からエクスポートすることでページごとに、または共有の [`+layout.js`](routing#layout-layout.js) や [`+layout.server.js`](routing#layout-layout.server.js) を使用してページグループごとに制御することが可能です。アプリ全体に対してオプションを定義するには、最上位のレイアウト(root layout)からそれをエクスポートします。子レイアウトとページは親レイアウトで設定された値を上書きするため、例えば、プリレンダリングをアプリ全体で有効にし、それから動的にレンダリングする必要があるページではそれを無効にすることができます。 アプリの様々な領域でこれらのオプションをうまく組み合わせることができます。例えば、マーケティングページは高速化を最大限にするためにプリレンダリングし、動的なページは SEO とアクセシビリティのためにサーバーでレンダリングし、管理者用のセクションはクライアントのみでレンダリングするようにして SPA にすることができます。このように、SvelteKit はとても万能で多くの用途にお使いいただけます。 ## prerender あなたのアプリの、少なくともいくつかのルートは、ビルド時に生成されるシンプルな HTML ファイルとして表現されることが多いでしょう。これらのルート(routes)を [_プリレンダリング_](glossary#Prerendering) することができます。 ```js /// file: +page.js/+page.server.js/+server.js export const prerender = true; ``` 代わりに、`export const prerender = true` を最上位(root)の `+layout.js` または `+layout.server.js` に設定し、明示的にプリレンダリングしないものとしてマークされたページを除き、全てをプリレンダリングできます: ```js /// file: +page.js/+page.server.js/+server.js export const prerender = false; ``` `prerender = true` があるルート(routes)は動的な SSR を行うのに使用する manifest から除外されるため、サーバー (または serverless/edge functions) を小さくすることができます。場合によっては、ルート(route)をプリレンダリングしつつ、manifest にも含めたいことがあるでしょう (例えば、`/blog/[slug]` のようなルート(route)があり、最も新しい/人気のあるコンテンツはプリレンダリングしたいがめったにアクセスされないものはサーバーでレンダリングしたい、など)。こういったケースのために、3つ目のオプションがあります、'auto' です: ```js /// file: +page.js/+page.server.js/+server.js export const prerender = 'auto'; ``` > [!NOTE] もしアプリ全体がプリレンダリングに適している場合は、[`adapter-static`](https://github.com/sveltejs/kit/tree/main/packages/adapter-static) を使うことで、任意の静的 Web サーバーで使用するのに適したファイルを出力することができます。 プリレンダラはアプリの最上位(root)から開始され、プリレンダリング可能なページや `+server.js` ルート(routes)を見つけると、そのファイルを生成します。各ページは、プリレンダリングの候補である他のページを指し示す `` 要素を見つけるためにスキャンされます。このため、通常はどのページにアクセスすべきか指定する必要はありません。もしプリレンダラがアクセスするページを指定する必要がある場合は、[`config.kit.prerender.entries`](configuration#prerender) で指定するか、動的なルート(route)から [`entries`](#entries) 関数をエクスポートします。 プリレンダリング中、[`$app/environment`]($app-environment) からインポートされる `building` の値は `true` になります。 ### Prerendering server routes 他のページオプションとは違い、`prerender` は `+server.js` ファイルにも適用できます。これらのファイルはレイアウトから影響を受けませんが、そこからデータを読み込むページからデフォルトの値を継承します。例えば、`+page.js` がこの `load` 関数を含む場合… ```js /// file: +page.js export const prerender = true; /** @type {import('./$types').PageLoad} */ export async function load({ fetch }) { const res = await fetch('/my-server-route.json'); return await res.json(); } ``` …それから `src/routes/my-server-route.json/+server.js` は、自身の `export const prerender = false` を含んでいなければ、プリレンダリング可能であると扱われることになります。 ### プリレンダリングしない場合 基本的なルールは次の通りです: ページがプリレンダリング可能であると言うためには、そのページを直接表示する2人のユーザーが、サーバーから同じコンテンツを取得できなけれなりません。 > [!NOTE] 全てのページがプリレンダリングに適しているわけではありません。プリレンダリングされたコンテンツは全てのユーザーに表示されます。もちろん、プリレンダリングされたページの `onMount` でパーソナライズされたデータをフェッチできますが、ブランクの初期コンテンツやローディングインジケーターにより、ユーザエクスペリエンスが低下してしまう可能性があります。 `src/routes/blog/[slug]/+page.svelte` ルート(route)のような、ページのパラメータを元にデータをロードするページもプリレンダリングができることにご注意ください。 プリレンダリング中に [`url.searchParams`](load#Using-URL-data-url) にアクセスすることは禁止されています。もし使う必要があるなら、ブラウザの中だけで行うようにしてください (例えば `onMount` の中で)。 [action](form-actions) 付きのページは、サーバーがその action の `POST` リクエストを処理できなければならないため、プリレンダリングできません。 ### ルートの衝突(Route conflicts) プリレンダリングはファイルシステムに書き込むため、ディレクトリとファイルが同じ名前になるエンドポイントを2つ持つことはできません。例えば、`src/routes/foo/+server.js` と `src/routes/foo/bar/+server.js` の場合は、`foo` と `foo/bar` を作成しようとしますが、これは不可能です。 このため(他にも理由はありますが)、常に拡張子を付けておくことを推奨します — `src/routes/foo.json/+server.js` と `src/routes/foo/bar.json/+server.js` は、`foo.json` と `foo/bar.json` ファイルが並んで調和して共存できます。 ページの場合は、`foo` ではなく `foo/index.html` を書き込むことでこの問題を回避しています。 ### Troubleshooting 'The following routes were marked as prerenderable, but were not prerendered' というようなエラーが表示されたら、それは該当のルート (またはページの場合は親レイアウト) に `export const prerender = true` があるにもかかわらず、プリレンダリングクローラーがそのページにアクセスせず、そのページがプリレンダリングされていないことが原因です。 これらのルート(route)は動的にサーバーレンダリングできないため、該当のルート(route)にアクセスしようとしたときにエラーが発生します。それを解決するには、いくつか方法があります: * SvelteKit が [`config.kit.prerender.entries`](configuration#prerender) か [`entries`](#entries) ページオプションからのリンクを辿ってそのルート(route)を見つけられるようにしてください。動的なルート(例えば `[parameters]` を持つページ) へのリンクは、他のエントリーポイントをクローリングしても見つからない場合はこのオプションに追加してください。そうしないと、SvelteKit はその parameters が持つべき値がわからないので、プリレンダリングされません。プリレンダリング可能(prerenderable)なページとしてマークされていないページは無視され、そのページから他のページ(プリレンダリング可能なものも含む)へのリンクもクローリングされません。 * サーバーサイドレンダリングが可能な別のプリレンダリングページで、該当のルートのリンクを検出できるようにしてください。 * `export const prerender = true` から `export const prerender = 'auto'` に変更してください。`'auto'` になっているルート(route)は動的にサーバーレンダリングすることができます ## entries SvelteKit は、 _エントリーポイント(entry points)_ を開始地点としてクローリングを行うことでページを自動的に発見します。デフォルトでは、動的でないルート(route)はすべてエントリーポイントとみなされます。例えば、以下のルートがある場合… ```bash / # non-dynamic /blog # non-dynamic /blog/[slug] # dynamic, because of `[slug]` ``` …SvelteKit は `/` と `/blog` をプリレンダリングし、その過程で `` などのリンクを発見し、それをプリレンダリング対象とします。 ほとんどの場合、これで十分です。しかし状況によっては、`/blog/hello-world` などのページに対するリンクが存在しない (あるいはプリレンダリングされたページには存在しない) 場合があります。この場合、SvelteKit にその存在を知らせる必要があります。 これを行うには [`config.kit.prerender.entries`](configuration#prerender) で指定するか、動的なルート(route) に属する `+page.js` か `+page.server.js` か `+server.js` で `entries` 関数をエクスポートします: ```js /// file: src/routes/blog/[slug]/+page.server.js /** @type {import('./$types').EntryGenerator} */ export function entries() { return [ { slug: 'hello-world' }, { slug: 'another-blog-post' } ]; } export const prerender = true; ``` `entries` は `async` 関数にすることができるので、(例えば) 上記で示したように CMS や データベースから投稿リストを取得することもできます。 ## ssr 通常、SvelteKit ではページを最初にサーバーでレンダリングし、その HTML をクライアントに送信して[ハイドレーション](glossary#Hydration)を行います。もし `ssr` を `false` に設定した場合、代わりに空の 'shell' ページがレンダリングされます。これはページがサーバーでレンダリングできない場合には便利 (例えば `document` などのブラウザオンリーな globals を使用するなど) ですが、ほとんどの状況では推奨されません ([appendix をご参照ください](glossary#SSR))。 ```js /// file: +page.js export const ssr = false; // If both `ssr` and `csr` are `false`, nothing will be rendered! ``` `export const ssr = false` を最上位(root)の `+layout.js` に追加した場合、アプリ全体がクライアントのみでレンダリングされるようになり、それはつまり、本質的にはアプリを SPA にする、ということを意味します。 > [!NOTE] Even with `ssr` set to `false`, code that relies on browser APIs should be imported in your `+page.svelte` or `+layout.svelte` file instead. This is because page options can be overriden and need to be evaluated by importing your `+page.js` or `+layout.js` file on the server (if you have a runtime) or at build time (in case of prerendering). ## csr 通常、SvelteKit はサーバーでレンダリングされた HTML を、クライアントサイドレンダリング(CSR)されたインタラクティブなページに [ハイドレーション](glossary#Hydration) します。JavaScript を全く必要としないページもあります。多くのブログ記事や 'about' ページがこのカテゴリに入ります。このような場合は CSR を無効にすることができます: ```js /// file: +page.js export const csr = false; // If both `csr` and `ssr` are `false`, nothing will be rendered! ``` CSR を無効にすると、クライアントに JavaScript が送信されません。つまり: * web ページは HTML と CSS だけで動作します。 * すべての Svelte コンポーネントの ` ``` ```svelte

Welcome {user().name}

``` > [!NOTE] We're passing a function into `setContext` to keep reactivity across boundaries. Read more about it [here](/docs/svelte/$state#Passing-state-into-functions) > [!LEGACY] > You also use stores from `svelte/store` for this, but when using Svelte 5 it is recommended to make use of universal reactivity instead. SSR でページがレンダリングされる場合、階層が深いページやコンポーネントで context ベースの state の値が更新されても、その親のコンポーネントの値には影響しません。なぜなら、その state の値が更新されるときには、親のコンポーネントはすでにレンダリング済みだからです。それとは対照的に、クライアントでは (CSR が有効な場合(デフォルトでは有効))、値は伝搬し、階層の上位にあるコンポーネントやページ、レイアウトに値が反映されます。従って、ハイドレーション中の state の更新による値の 'ちらつき(flashing)' を避けるため、通常は state を上から下にコンポーネントに渡すことを推奨します。 SSR を使用していない場合 (そして将来的にも SSR を使用する必要がないという保証がある場合) は、context API を使用しなくても、共有されるモジュールの中で state を安全に保持することができます。 ## コンポーネントとページの state は保持される アプリケーションの中を移動するとき、SvelteKit はすでに存在するレイアウトやページコンポーネントを再利用します。例えば、このようなルート(route)があるとして… ```svelte

{data.title}

Reading time: {Math.round(estimatedReadingTime)} minutes

{@html data.content}
``` …`/blog/my-short-post` から `/blog/my-long-post` への移動は、レイアウトやページ、コンポーネントの破棄や再作成を引き起こしません。代わりに、この `data` prop (と `data.title` と `data.content`) は更新されますが (他の Svelte コンポーネントも同様に)、コードは再実行されないため、`onMount` や `onDestroy` のようなライフサイクルメソッドは再実行されず、`estimatedReadingTime` も再計算されません。 代わりに、その値を [_リアクティブ_](/tutorial/svelte/state) にする必要があります: ```svelte /// file: src/routes/blog/[slug]/+page.svelte ``` > [!NOTE] `onMount` や `onDestroy` にあるコードをナビゲーションのあとに再実行する必要がある場合は、[afterNavigate]($app-navigation#afterNavigate) や [beforeNavigate]($app-navigation#beforeNavigate) をそれぞれ使用します。 このようにコンポーネントを再利用すると、サイドバースクロールの state などが保持され、変化する値の間で簡単にアニメーションを行うことができます。ナビゲーション時にコンポーネントを完全に破棄して再マウントする必要がある場合には、このパターンを使用できます: ```svelte {#key page.url.pathname} {/key} ``` ## state を URL に保存する もし、テーブルのフィルターやソートルールなどのように、リロード後も保持されるべき state、または SSR に影響を与える state がある場合、URL search パラメータ (例: `?sort=price&order=ascending`) はこれらを置くのに適した場所です。これらは `
` や `` の属性に置いたり、`goto('?key=value')` を使用してプログラム的に設定することもできます。`load` 関数の中では `url` パラメータを使用してアクセスでき、コンポーネントの中では `page.url.searchParams` でアクセスできます。 ## 一時的な state は snapshots に保存する 'アコーディオンは開いているか?' などの一部の UI の state は一時的なものですぐに捨てられます — ユーザーがページを移動したり更新したりして、その state が失われたとしてもそれほど問題ではありません。ユーザーが別のページに移動して戻ってきたときにデータを保持しておきたい場合もありますが、そのような state を URL や database に保存するのは行き過ぎでしょう。そういった場合のために、SvelteKit [snapshots](snapshots) を提供しています。これによってコンポーネントの state を履歴エントリーに関連付けることができます。 # アプリをビルドする SvelteKit アプリのビルドは2つのステージで行われます。どちらも `vite build` (通常は `npm run build` を経由します) を実行したときに行われます。 まず最初に、Vite がサーバーのコード、ブラウザのコード、service worker(もしあれば) の、最適化された本番向けビルドを作成します。必要に応じて、このステージで [プリレンダリング](page-options#prerender) が実行されます。 次に、*adapter* がこの本番向けビルドをあなたがデプロイしたいターゲットの環境向けに調整します — これについての詳細は以降のページにございます。 ## ビルド中に SvelteKit はビルド中に、解析のために `+page/layout(.server).js` ファイル (とそこにインポートされている全てのファイル) を読み込みます。このステージで読み込まれるべきでないコードがある場合は、[`$app/environment`]($app-environment) からインポートする `building` が `false` であることをチェックするコードを追加してください: ```js +++import { building } from '$app/environment';+++ import { setupMyDatabase } from '$lib/server/database'; +++if (!building) {+++ setupMyDatabase(); +++}+++ export function load() { // ... } ``` ## アプリのプレビュー ビルド後、`vite preview` (`npm run preview` 経由) を使用してローカルで本番向けビルドを確認することができます。これは Node 上でアプリを実行しているので、デプロイされるアプリの完全な再現ではないことにご注意ください。[`platform` オブジェクト](adapters#Platform-specific-context) などの adapter 固有の調整はプレビューには適用されません。 # Adapters SvelteKit アプリをデプロイする前に、それをデプロイ先の環境に _合わせる(adapt)_ 必要があります。adapter は、ビルドされたアプリをインプットとして受け取りデプロイ用のアウトプットを生成する小さなプラグインです。 様々なプラットフォーム向けの公式の adapter があります。これらについて以降のページにドキュメントがあります。 - [`@sveltejs/adapter-cloudflare`](adapter-cloudflare) for Cloudflare Pages - [`@sveltejs/adapter-cloudflare-workers`](adapter-cloudflare-workers) for Cloudflare Workers - [`@sveltejs/adapter-netlify`](adapter-netlify) for Netlify - [`@sveltejs/adapter-node`](adapter-node) for Node servers - [`@sveltejs/adapter-static`](adapter-static) for static site generation (SSG) - [`@sveltejs/adapter-vercel`](adapter-vercel) for Vercel 加えて、他のプラットフォーム向けに、[コミュニティによって提供されている adapter](https://sveltesociety.dev/packages?category=sveltekit-adapters) もございます。 ## adapter を使用する adapter は `svelte.config.js` に指定します。 ```js /// file: svelte.config.js // @filename: ambient.d.ts declare module 'svelte-adapter-foo' { const adapter: (opts: any) => import('@sveltejs/kit').Adapter; export default adapter; } // @filename: index.js // ---cut--- import adapter from 'svelte-adapter-foo'; /** @type {import('@sveltejs/kit').Config} */ const config = { kit: { adapter: adapter({ // adapter options go here }) } }; export default config; ``` ## プラットフォーム固有の情報 adapter によっては、リクエストに関する追加情報にアクセスすることができます。例えば、Cloudflare Workers の場合は KV namespaces などを含む `env` オブジェクトにアクセスできます。これは [hooks](hooks) や [サーバールート(server routes)](routing#server) で使用される `RequestEvent` に、`platform` プロパティとして渡されます。詳しくは、各 adapter のドキュメントをご参照ください。 # ゼロコンフィグデプロイ `npx sv create` で新しい SvelteKit プロジェクトを作成した場合、デフォルトで [`adapter-auto`](https://github.com/sveltejs/kit/tree/main/packages/adapter-auto) がインストールされます。この adapter はデプロイ時にサポートされている環境に合った adapter を自動でインストールします: - [`@sveltejs/adapter-cloudflare`](adapter-cloudflare) for [Cloudflare Pages](https://developers.cloudflare.com/pages/) - [`@sveltejs/adapter-netlify`](adapter-netlify) for [Netlify](https://netlify.com/) - [`@sveltejs/adapter-vercel`](adapter-vercel) for [Vercel](https://vercel.com/) - [`svelte-adapter-azure-swa`](https://github.com/geoffrich/svelte-adapter-azure-swa) for [Azure Static Web Apps](https://docs.microsoft.com/en-us/azure/static-web-apps/) - [`svelte-kit-sst`](https://github.com/sst/sst/tree/master/packages/svelte-kit-sst) for [AWS via SST](https://sst.dev/docs/start/aws/svelte) - [`@sveltejs/adapter-node`](adapter-node) for [Google Cloud Run](https://cloud.google.com/run) デプロイするターゲットの環境が決まったら、`devDependencies` に適切な adapter をインストールすることを推奨します。これにより、lockfile に adapter が追加されるため、CI でのインストール時間が少し改善されます。 ## 環境固有の設定 [`adapter-vercel`](adapter-vercel) や [`adapter-netlify`](adapter-netlify) の `{ edge: true }` のような設定オプションを追加したければ、そのオプションを持つ adapter をインストールしなければなりません。`adapter-auto` はそれらのオプションを受け付けません。 ## コミュニティ adapter を追加する 追加の adapter 向けのゼロコンフィグサポートを追加するには、[adapters.js](https://github.com/sveltejs/kit/blob/main/packages/adapter-auto/adapters.js) を編集し、pull request を開きます。 # Node サーバー スタンドアロンな Node サーバーを作る場合は、[`adapter-node`](https://github.com/sveltejs/kit/tree/main/packages/adapter-node) を使います。 ## 使い方 `npm i -D @sveltejs/adapter-node` を実行してインストールし、`svelte.config.js` にこの adapter を追加します: ```js // @errors: 2307 /// file: svelte.config.js import adapter from '@sveltejs/adapter-node'; export default { kit: { adapter: adapter() } }; ``` ## デプロイ(Deploying) まず、`npm run build` でアプリをビルドします。これによって adapter のオプションで指定した出力ディレクトリ (デフォルトは `build`) に本番環境用のサーバーが作成されます。 アプリケーションを実行するには、出力ディレクトリ、プロジェクトの `package.json`、`node_modules` の本番向けの依存関係(production dependencies)が必要です。本番向けの依存関係は、`package.json` と `package-lock.json` をコピーしてから `npm ci --omit dev` を実行すると生成することができます (あなたのアプリが何の依存関係も持たない場合はこのステップをスキップできます)。そして、このコマンドでアプリを起動することができます: ```bash node build ``` [Rollup](https://rollupjs.org) を使うと開発用の依存関係(Development dependencies)もアプリにバンドルされます。パッケージをバンドルするか外部化するかコントロールするには、そのパッケージを `package.json` の `devDependencies` か `dependencies` にそれぞれ配置します。 ### レスポンスの圧縮 通常、サーバーからのレスポンスを圧縮したいでしょう。SSL やロードバランシングのためにリバースプロキシの後ろにサーバーを配置している場合は、通常はそちらのレイヤーで圧縮を処理したほうがパフォーマンスの向上につながります。Node.js はシングルスレッドだからです。 しかし、あなたが [custom server](#Custom-server) を構築していて、そこに圧縮用のミドルウェアを追加したい場合は、[`@polka/compression`](https://www.npmjs.com/package/@polka/compression) を使用することをおすすめします。SvelteKit はレスポンスをストリーミングしますが、一般的な `compression` パッケージはストリーミングをサポートしていないため、使用するとエラーとなる可能性があります。 ## 環境変数 `dev` と `preview` のときは、SvelteKit は `.env` ファイル (または `.env.local` や `.env.[mode]`、[Vite によって決定されているもの](https://vitejs.dev/guide/env-and-mode.html#env-files)) から環境変数を読み取ります。 プロダクションでは、`.env` ファイルは自動的に読み取れらません。そうするには、プロジェクトに `dotenv` をインストールします… ```bash npm install dotenv ``` …そしてビルドされたアプリを実行する前にそれを呼び出します: ```bash node +++-r dotenv/config+++ build ``` If you use Node.js v20.6+, you can use the [`--env-file`](https://nodejs.org/en/learn/command-line/how-to-read-environment-variables-from-nodejs) flag instead: ```bash node +++--env-file=.env+++ build ``` ### `PORT`、`HOST`、`SOCKET_PATH` デフォルトでは、サーバーは `0.0.0.0`、port 3000 でコネクションを受け付けます。これは環境変数の `PORT` と `HOST` を使ってカスタマイズすることができます。 ``` HOST=127.0.0.1 PORT=4000 node build ``` その他の方法としては、指定したソケットパスでコネクションを受け付けるようサーバーを設定することができます。環境変数の `SOCKET_PATH` を使用して設定する場合、環境変数の `HOST` と `PORT` は無視されます。 ``` SOCKET_PATH=/tmp/socket node build ``` ### `ORIGIN`、`PROTOCOL_HEADER`、`HOST_HEADER`、`PORT_HEADER` HTTP は SvelteKit に現在リクエストされている URL を知るための信頼できる方法を提供しません。アプリがホストされている場所を Sveltekit に伝える最も簡単な方法は、環境変数 `ORIGIN` を設定することです: ``` ORIGIN=https://my.site node build # or e.g. for local previewing and testing ORIGIN=http://localhost:3000 node build ``` これにより、パス名 `/stuff` に対するリクエストは正しく `https://my.site/stuff` に解決されます。別の方法として、リクエストプロトコルとホストを SvelteKit に伝えるヘッダーを指定し、そこから origin URL を組み立てることもできます: ``` PROTOCOL_HEADER=x-forwarded-proto HOST_HEADER=x-forwarded-host node build ``` > [!NOTE] [`x-forwarded-proto`](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/X-Forwarded-Proto) と [`x-forwarded-host`](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/X-Forwarded-Host) は事実上の標準となっているヘッダーで、リバースプロキシー (ロードバランサーや CDN などを考えてみてください) を使用している場合に、オリジナルのプロトコルとホストを転送します。これらの変数は、あなたのサーバーが信頼できるリバースプロキシーの後ろにある場合にのみ設定すべきです。そうしないと、クライアントがこれらのヘッダーを偽装することが可能になってしまいます。 > > プロキシーを非標準の port でホストしていて、リバースプロキシ−が `x-forwarded-port` をサポートしている場合は、`PORT_HEADER=x-forwarded-port` を設定することもできます。 `adapter-node` があなたのデプロイの URL を正しく判断することができない場合、[form actions](form-actions) を使用するとこのエラーが発生することがあります: > [!NOTE] クロスサイトの POST フォーム送信は禁止されています ### `ADDRESS_HEADER` と `XFF_DEPTH` hooks とエンドポイントに渡される [RequestEvent](@sveltejs-kit#RequestEvent) オブジェクトにはクライアントの IP アドレスを返す `event.getClientAddress()` 関数が含まれています。デフォルトでは、これは接続中の `remoteAddress` です。もしサーバーが1つ以上のプロキシー (例えばロードバランサー) の後ろにある場合、この値はクライアントの IP アドレスではなく、最も内側にあるプロキシーの IP アドレスを含むことになるため、アドレスを読み取るために `ADDRESS_HEADER` を指定する必要があります: ``` ADDRESS_HEADER=True-Client-IP node build ``` > [!NOTE] ヘッダーは簡単に偽装されます。`PROTOCOL_HEADER` や `HOST_HEADER` と同様、これらを設定する前に[自分が何をしているのか知るべき](https://adam-p.ca/blog/2022/03/x-forwarded-for/)です。 `ADDRESS_HEADER` が `X-Forwarded-For` の場合、ヘッダーの値にはカンマで区切られた IP アドレスのリストが含まれます。環境変数 `XFF_DEPTH` には、あなたのサーバーの前に信頼できるプロキシーがいくつあるか指定する必要があります。例えば、3つの信頼できるプロキシーがある場合、プロキシー3はオリジナルのコネクションと最初の2つのプロキシーのアドレスを転送します: ``` , , ``` 一番左のアドレスを読め、というガイドもありますが、これだと[スプーフィング(なりすまし)に対し脆弱](https://adam-p.ca/blog/2022/03/x-forwarded-for/)なままです: ``` , , , ``` 代わりに、信頼できるプロキシーの数を考慮して*右*から読み込みます。この場合、`XFF_DEPTH=3` を使用します。 > [!NOTE] もし、一番左のアドレスを読む必要がある場合 (そしてスプーフィングを気にしない場合) — 例えば、位置情報サービスを提供する場合、つまり IP アドレスが*信頼できる*ことよりも*リアル*であることが重要な場合、アプリの中で `x-forwarded-for` ヘッダーを検査することでそれが可能です。 ### `BODY_SIZE_LIMIT` ストリーミング中も含め、受け付けるリクエストボディの最大サイズを byte で指定します。ボディサイズの指定には単位を使用することができます。キロバイトには (`K`)、メガバイトには (`M`)、ギガバイトには (`G`) です。例えば、`512K` や `1M` のようにします。デフォルトは 512キロバイト です。もっと高度な設定が必要な場合は、このオプションの値を `Infinity` (adapter が古いバージョンの場合は 0) にして無効化し、[`handle`](hooks#Server-hooks-handle) にカスタムのチェックを実装することができます。 ### `SHUTDOWN_TIMEOUT` The number of seconds to wait before forcefully closing any remaining connections after receiving a `SIGTERM` or `SIGINT` signal. Defaults to `30`. Internally the adapter calls [`closeAllConnections`](https://nodejs.org/api/http.html#servercloseallconnections). See [Graceful shutdown](#Graceful-shutdown) for more details. ### `IDLE_TIMEOUT` When using systemd socket activation, `IDLE_TIMEOUT` specifies the number of seconds after which the app is automatically put to sleep when receiving no requests. If not set, the app runs continuously. See [Socket activation](#Socket-activation) for more details. ## Options この adapter は様々なオプションで設定を行うことができます: ```js // @errors: 2307 /// file: svelte.config.js import adapter from '@sveltejs/adapter-node'; export default { kit: { adapter: adapter({ // default options are shown out: 'build', precompress: true, envPrefix: '' }) } }; ``` ### out サーバーをビルドするディレクトリです。デフォルトは `build` です。つまり、`node build` を指定すると、サーバが作成されローカルで起動します。 ### precompress アセットやプリレンダリングされたページを gzip や brotli を使って事前圧縮(precompress)するのを有効にします。デフォルトは `true` です。 ### envPrefix デプロイの設定に使用される環境変数の名前を変更する必要がある場合 (例えば、あなたのコントロール下にない環境変数との競合を解消するため)、接頭辞(prefix)を指定することができます: ```js envPrefix: 'MY_CUSTOM_'; ``` ```sh MY_CUSTOM_HOST=127.0.0.1 \ MY_CUSTOM_PORT=4000 \ MY_CUSTOM_ORIGIN=https://my.site \ node build ``` ## Graceful shutdown By default `adapter-node` gracefully shuts down the HTTP server when a `SIGTERM` or `SIGINT` signal is received. It will: 1. reject new requests ([`server.close`](https://nodejs.org/api/http.html#serverclosecallback)) 2. wait for requests that have already been made but not received a response yet to finish and close connections once they become idle ([`server.closeIdleConnections`](https://nodejs.org/api/http.html#servercloseidleconnections)) 3. and finally, close any remaining connections that are still active after [`SHUTDOWN_TIMEOUT`](#Environment-variables-SHUTDOWN_TIMEOUT) seconds. ([`server.closeAllConnections`](https://nodejs.org/api/http.html#servercloseallconnections)) > [!NOTE] If you want to customize this behaviour you can use a [custom server](#Custom-server). You can listen to the `sveltekit:shutdown` event which is emitted after the HTTP server has closed all connections. Unlike Node's `exit` event, the `sveltekit:shutdown` event supports asynchronous operations and is always emitted when all connections are closed even if the server has dangling work such as open database connections. ```js // @errors: 2304 process.on('sveltekit:shutdown', async (reason) => { await jobs.stop(); await db.close(); }); ``` The parameter `reason` has one of the following values: - `SIGINT` - shutdown was triggered by a `SIGINT` signal - `SIGTERM` - shutdown was triggered by a `SIGTERM` signal - `IDLE` - shutdown was triggered by [`IDLE_TIMEOUT`](#Environment-variables-IDLE_TIMEOUT) ## Socket activation Most Linux operating systems today use a modern process manager called systemd to start the server and run and manage services. You can configure your server to allocate a socket and start and scale your app on demand. This is called [socket activation](http://0pointer.de/blog/projects/socket-activated-containers.html). In this case, the OS will pass two environment variables to your app — `LISTEN_PID` and `LISTEN_FDS`. The adapter will then listen on file descriptor 3 which refers to a systemd socket unit that you will have to create. > [!NOTE] You can still use [`envPrefix`](#Options-envPrefix) with systemd socket activation. `LISTEN_PID` and `LISTEN_FDS` are always read without a prefix. To take advantage of socket activation follow these steps. 1. Run your app as a [systemd service](https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html). It can either run directly on the host system or inside a container (using Docker or a systemd portable service for example). If you additionally pass an [`IDLE_TIMEOUT`](#Environment-variables-IDLE_TIMEOUT) environment variable to your app it will gracefully shutdown if there are no requests for `IDLE_TIMEOUT` seconds. systemd will automatically start your app again when new requests are coming in. ```ini /// file: /etc/systemd/system/myapp.service [Service] Environment=NODE_ENV=production IDLE_TIMEOUT=60 ExecStart=/usr/bin/node /usr/bin/myapp/build ``` 2. Create an accompanying [socket unit](https://www.freedesktop.org/software/systemd/man/latest/systemd.socket.html). The adapter only accepts a single socket. ```ini /// file: /etc/systemd/system/myapp.socket [Socket] ListenStream=3000 [Install] WantedBy=sockets.target ``` 3. Make sure systemd has recognised both units by running `sudo systemctl daemon-reload`. Then enable the socket on boot and start it immediately using `sudo systemctl enable --now myapp.socket`. The app will then automatically start once the first request is made to `localhost:3000`. ## カスタムサーバー この adapter は、ビルドのディレクトリに2つのファイルを作成します — `index.js` と `handler.js` です。デフォルトのビルドのディレクトリを使用している場合、`node build` などで `index.js` を実行すると、設定された port でサーバーが起動されます。 別の方法として、[Express](https://github.com/expressjs/express)、[Connect](https://github.com/senchalabs/connect)、[Polka](https://github.com/lukeed/polka) (またはビルトインの [`http.createServer`](https://nodejs.org/dist/latest/docs/api/http.html#httpcreateserveroptions-requestlistener)) を使用するためのハンドラーをエクスポートする `handler.js` ファイルをインポートし、独自のサーバーをセットアップすることもできます。 ```js // @errors: 2307 7006 /// file: my-server.js import { handler } from './build/handler.js'; import express from 'express'; const app = express(); // add a route that lives separately from the SvelteKit app app.get('/healthcheck', (req, res) => { res.end('ok'); }); // let SvelteKit handle everything else, including serving prerendered pages and static assets app.use(handler); app.listen(3000, () => { console.log('listening on port 3000'); }); ``` # Static site generation SvelteKit を static site generator (SSG) として使用するには、[`adapter-static`](https://github.com/sveltejs/kit/tree/main/packages/adapter-static) を使用します。 この adapter はサイト全体を静的なファイルのコレクションとしてプリレンダリングします。もし、一部のページのみをプリレンダリングして他のページは動的にサーバーでレンダリングしたい場合、別の adapter と [`prerender` オプション](page-options#prerender) を組み合わせて使用する必要があります。 ## 使い方 `npm i -D @sveltejs/adapter-static` を実行してインストールし、`svelte.config.js` にこの adapter を追加します: ```js // @errors: 2307 /// file: svelte.config.js import adapter from '@sveltejs/adapter-static'; export default { kit: { adapter: adapter({ // default options are shown. On some platforms // these options are set automatically — see below pages: 'build', assets: 'build', fallback: undefined, precompress: false, strict: true }) } }; ``` …そして [`prerender`](page-options#prerender) オプションを最上位のレイアウト(root layout)に追加します: ```js /// file: src/routes/+layout.js // This can be false if you're using a fallback (i.e. SPA mode) export const prerender = true; ``` > [!NOTE] SvelteKit の [`trailingSlash`](page-options#trailingSlash) オプションを、あなたの環境に対して適切に設定しなければなりません。もしあなたのホスト環境が、`/a` へのリクエストを受け取ったときに `/a.html` をレンダリングしない場合、`/a/index.html` を作成するために最上位のレイアウト(root layout)で `trailingSlash: 'always'` を設定する必要があります。 ## ゼロコンフィグサポート ゼロコンフィグサポートがあるプラットフォームもあります (将来増える予定): - [Vercel](https://vercel.com) これらのプラットフォームでは、adapter のオプションを省略することで、`adapter-static` が最適な設定を提供できるようになります: ```js // @errors: 2304 /// file: svelte.config.js export default { kit: { adapter: adapter(---{...}---) } }; ``` ## Options ### pages プリレンダリングされたページを書き込むディレクトリです。デフォルトは `build` です。 ### assets 静的なアセット (`static` のコンテンツと、SvelteKit が生成するクライアントサイドの JS と CSS) を書き込むディレクトリです。通常は `pages` と同じにするべきで、デフォルトでは、それがどんな値だったとしても、`pages` の値になります。しかし、まれな状況では、出力されるページとアセットを別々の場所にする必要があるかもしれません。 ### fallback [SPA モード](single-page-apps)向けにフォールバックページ(fallback page)を指定します。例えば、`index.html` や `200.html`、`404.html` などです。 ### precompress `true` の場合、brotli や gzip でファイルを事前圧縮(precompress)します。これによって `.br` ファイルや `.gz` ファイルが生成されます。 ### strict デフォルトでは `adapter-static` は、アプリの全てのページとエンドポイント (もしあれば) がプリレンダリングされているか、もしくは `fallback` オプションが設定されているかをチェックします。このチェックは、アプリの一部が最終的な出力に含まれずアクセスできない状態なのに誤って公開されてしまうのを防ぐために存在します。もし、それでも良いことがわかっている場合 (例えばあるページが条件付きでしか存在しない場合)、`strict` を `false` に設定してこのチェックをオフにすることができます。 ## GitHub Pages [GitHub Pages](https://docs.github.com/en/pages/getting-started-with-github-pages/about-github-pages) 向けにビルドするとき、あなたのリポジトリの名前が `your-username.github.io` と異なる場合は、[`config.kit.paths.base`](configuration#paths) をあなたのリポジトリの名前に更新してください。サイトが root からではなく `https://your-username.github.io/your-repo-name` から提供されるためです。 GitHub Pages が提供するデフォルトの 404 ページを置き換えるためにフォールバック用の `404.html` ページを生成することもあるでしょう。 GitHub Pages 向けの設定は以下のようになるでしょう: ```js // @errors: 2307 2322 /// file: svelte.config.js import adapter from '@sveltejs/adapter-static'; /** @type {import('@sveltejs/kit').Config} */ const config = { kit: { adapter: adapter({ fallback: '404.html' }), paths: { base: process.argv.includes('dev') ? '' : process.env.BASE_PATH } } }; export default config; ``` GitHub actions を使用して、サイトが変更されたときに自動で GitHub Pages にデプロイすることができます。サンプルの workflow はこちらです: ```yaml ### file: .github/workflows/deploy.yml name: Deploy to GitHub Pages on: push: branches: 'main' jobs: build_site: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 # If you're using pnpm, add this step then change the commands and cache key below to use `pnpm` # - name: Install pnpm # uses: pnpm/action-setup@v3 # with: # version: 8 - name: Install Node.js uses: actions/setup-node@v4 with: node-version: 20 cache: npm - name: Install dependencies run: npm install - name: build env: BASE_PATH: '/${{ github.event.repository.name }}' run: | npm run build - name: Upload Artifacts uses: actions/upload-pages-artifact@v3 with: # this should match the `pages` option in your adapter-static options path: 'build/' deploy: needs: build_site runs-on: ubuntu-latest permissions: pages: write id-token: write environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} steps: - name: Deploy id: deployment uses: actions/deploy-pages@v4 ``` If you're not using GitHub actions to deploy your site (for example, you're pushing the built site to its own repo), add an empty `.nojekyll` file in your `static` directory to prevent Jekyll from interfering. # Single-page apps SvelteKit アプリは、どんな adapter を使っていても、最上位のレイアウト(root layout)で SSR を無効にすることで、完全にクライアントレンダリングされるシングルページアプリ (SPA) にすることができます。 ```js /// file: src/routes/+layout.js export const ssr = false; ``` > [!NOTE] ほとんどの場合、これはおすすめできません: SEO に悪影響を与え、知覚的なパフォーマンスが低下する傾向があり、もし JavaScript が失敗したり無効になっていたりする場合 (これは[あなたが思うより頻繁に](https://kryogenix.org/code/browser/everyonehasjs.html)発生します)、ユーザーはアプリにアクセスできなくなります. サーバーサイドのロジック (すなわち `+page.server.js`、`+layout.server.js`、`+server.js` ファイル) がない場合は、[`adapter-static`](adapter-static) を使い _フォールバックページ(fallback page)_ を追加することで SPA を作ることができます。 ## 使い方 `npm i -D @sveltejs/adapter-static` でインストールし、それから `svelte.config.js` にこの adapter と以下のオプションを追加します: ```js // @errors: 2307 /// file: svelte.config.js import adapter from '@sveltejs/adapter-static'; export default { kit: { adapter: adapter({ fallback: '200.html' // may differ from host to host }) } }; ``` フォールバックページ(`fallback` page)とは、SvelteKit がページテンプレート(例: `app.html`)から作成する HTML ページで、アプリをロードし正しいルート(routes)にナビゲートします。例えば、静的 web ホスティングである [Surge](https://surge.sh/help/adding-a-200-page-for-client-side-routing) では、静的なアセットやプリレンダリングページに対応しないリクエストを処理する `200.html` ファイルを追加する必要があります。 ホスティング環境によっては `index.html` であったり全く別のものであったりします — 使いたいプラットフォームのドキュメントをご参照ください。 > [!NOTE] フォールバックページには [`paths.relative`](configuration#paths) の値に関係なく常にアセットの絶対パス (つまり `.` ではなく `/` で始まる) が含まれることにご注意ください。フォールバックページは任意のパスのリクエストに応答するために使用されるからです。 ## Apache SPA を [Apache](https://httpd.apache.org/) で実行する場合は、`static/.htaccess` ファイルを追加し、リクエストをフォールバックページ(fallback page)にルーティングする必要があります: ``` RewriteEngine On RewriteBase / RewriteRule ^200\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /200.html [L] ``` ## ページを個別にプリレンダリングする 特定のページをプリレンダリングしたい場合、アプリのその部分だけ `ssr` と `prerender` を再び有効にします: ```js /// file: src/routes/my-prerendered-page/+page.js export const prerender = true; export const ssr = true; ``` # Cloudflare Pages [Cloudflare Pages](https://developers.cloudflare.com/pages/) にデプロイする場合は、[`adapter-cloudflare`](https://github.com/sveltejs/kit/tree/main/packages/adapter-cloudflare) を使用します。 This adapter will be installed by default when you use [`adapter-auto`](adapter-auto). If you plan on staying with Cloudflare Pages, you can switch from [`adapter-auto`](adapter-auto) to using this adapter directly so that values specific to Cloudflare Workers are emulated during local development, type declarations are automatically applied, and the ability to set Cloudflare-specific options is provided. ## 比較 - `adapter-cloudflare` – SvelteKit の全ての機能をサポートします; [Cloudflare Pages](https://blog.cloudflare.com/cloudflare-pages-goes-full-stack/) 向けにビルドします - `adapter-cloudflare-workers` – SvelteKit の全ての機能をサポートします; Cloudflare Workers 向けにビルドします - `adapter-static` – クライアントサイドの静的なアセットを生成するのみです; Cloudflare Pages と互換性があります ## 使い方 `npm i -D @sveltejs/adapter-cloudflare` を実行してインストールし、`svelte.config.js` にこの adapter を追加します: ```js // @errors: 2307 /// file: svelte.config.js import adapter from '@sveltejs/adapter-cloudflare'; export default { kit: { adapter: adapter({ // See below for an explanation of these options routes: { include: ['/*'], exclude: [''] }, platformProxy: { configPath: 'wrangler.toml', environment: undefined, experimentalJsonConfig: false, persist: false } }) } }; ``` ## Options ### routes Allows you to customise the [`_routes.json`](https://developers.cloudflare.com/pages/platform/functions/routing/#create-a-_routesjson-file) file generated by `adapter-cloudflare`. - `include` defines routes that will invoke a function, and defaults to `['/*']` - `exclude` defines routes that will _not_ invoke a function — this is a faster and cheaper way to serve your app's static assets. This array can include the following special values: - `` contains your app's build artifacts (the files generated by Vite) - `` contains the contents of your `static` directory - `` contains a list of prerendered pages - `` (the default) contains all of the above You can have up to 100 `include` and `exclude` rules combined. Generally you can omit the `routes` options, but if (for example) your `` paths exceed that limit, you may find it helpful to manually create an `exclude` list that includes `'/articles/*'` instead of the auto-generated `['/articles/foo', '/articles/bar', '/articles/baz', ...]`. ### platformProxy Preferences for the emulated `platform.env` local bindings. See the [getPlatformProxy](https://developers.cloudflare.com/workers/wrangler/api/#syntax) Wrangler API documentation for a full list of options. ## デプロイメント(Deployment) Cloudflare Pages の始め方は、[Get Started Guide](https://developers.cloudflare.com/pages/get-started) に従ってください。 プロジェクトのセッティングを設定するときは、以下のセッティングを使用しなければなりません: - **Framework preset** – SvelteKit - **Build command** – `npm run build` or `vite build` - **Build output directory** – `.svelte-kit/cloudflare` ## Runtime APIs [`env`](https://developers.cloudflare.com/workers/runtime-apis/fetch-event#parameters) オブジェクトにはあなたのプロジェクトの [bindings](https://developers.cloudflare.com/pages/platform/functions/bindings/) が含まれており、KV/DO namespaces などで構成されています。これは `platform` プロパティを介して [`context`](https://developers.cloudflare.com/workers/runtime-apis/handlers/fetch/#contextwaituntil)、[`caches`](https://developers.cloudflare.com/workers/runtime-apis/cache/)、[`cf`](https://developers.cloudflare.com/workers/runtime-apis/request/#the-cf-property-requestinitcfproperties) と一緒に SvelteKit に渡されます。つまり、hooks とエンドポイントでこれらにアクセスできるということです: ```js // @errors: 7031 export async function POST({ request, platform }) { const x = platform.env.YOUR_DURABLE_OBJECT_NAMESPACE.idFromName('x'); } ``` > [!NOTE] 環境変数については、SvelteKit のビルトインの `$env` モジュールを優先的に使用したほうが良いでしょう。 これらの型をアプリで使用できるようにするには、`@cloudflare/workers-types` をインストールし、`src/app.d.ts` でこれらを参照します: ```ts /// file: src/app.d.ts +++import { KVNamespace, DurableObjectNamespace } from '@cloudflare/workers-types';+++ declare global { namespace App { interface Platform { +++ env?: { YOUR_KV_NAMESPACE: KVNamespace; YOUR_DURABLE_OBJECT_NAMESPACE: DurableObjectNamespace; };+++ } } } export {}; ``` ### Testing Locally Cloudflare Workers specific values in the `platform` property are emulated during dev and preview modes. Local [bindings](https://developers.cloudflare.com/workers/wrangler/configuration/#bindings) are created based on the configuration in your `wrangler.toml` file and are used to populate `platform.env` during development and preview. Use the adapter config [`platformProxy` option](#Options-platformProxy) to change your preferences for the bindings. For testing the build, you should use [wrangler](https://developers.cloudflare.com/workers/cli-wrangler) **version 3**. Once you have built your site, run `wrangler pages dev .svelte-kit/cloudflare`. ## Notes プロジェクトの root にある `/functions` ディレクトリに含まれる関数はデプロイメントには含まれず、[1つの `_worker.js` ファイル](https://developers.cloudflare.com/pages/platform/functions/#advanced-mode)にコンパイルされます。関数は、あなたの SvelteKit アプリの [サーバーエンドポイント(server endpoints)](routing#server) として実装する必要があります。 Cloudflare Pages 固有の `_headers` ファイルと `_redirects` ファイルについては、`/static` フォルダに置くことで、静的アセットのレスポンス (画像など) に使用することができます。 しかし、SvelteKit が動的にレンダリングするレスポンスには効果がありません。この場合にカスタムヘッダーやリダイレクトレスポンスを返すには、[サーバーエンドポイント(server endpoints)](routing#server) や [`handle`](hooks#Server-hooks-handle) hook から返す必要があります。 ## トラブルシューティング ### 外部の資料 [Cloudflare の、SvelteKit サイトのデプロイに関するドキュメント](https://developers.cloudflare.com/pages/framework-guides/deploy-a-svelte-kit-site)をご参照ください。 ### ファイルシステムにアクセスする Cloudflare Workers では `fs` を使用することはできません。そうする必要があるルート(route)については[プリレンダリング](page-options#prerender)する必要があります。 # Cloudflare Workers [Cloudflare Workers](https://workers.cloudflare.com/) にデプロイする場合は、[`adapter-cloudflare-workers`](https://github.com/sveltejs/kit/tree/main/packages/adapter-cloudflare-workers) を使用します。 > [!NOTE] Unless you have a specific reason to use `adapter-cloudflare-workers`, it's recommended that you use `adapter-cloudflare` instead. Both adapters have equivalent functionality, but Cloudflare Pages offers features like GitHub integration with automatic builds and deploys, preview deployments, instant rollback and so on. ## 使い方 `npm i -D @sveltejs/adapter-cloudflare-workers` を実行してインストールし、`svelte.config.js` にこの adapter を追加します: ```js // @errors: 2307 /// file: svelte.config.js import adapter from '@sveltejs/adapter-cloudflare-workers'; export default { kit: { adapter: adapter({ config: 'wrangler.toml', platformProxy: { configPath: 'wrangler.toml', environment: undefined, experimentalJsonConfig: false, persist: false } }) } }; ``` ## Options ### config Path to your custom `wrangler.toml` or `wrangler.json` config file. ### platformProxy Preferences for the emulated `platform.env` local bindings. See the [getPlatformProxy](https://developers.cloudflare.com/workers/wrangler/api/#syntax) Wrangler API documentation for a full list of options. ## 基本設定 この adapter では、プロジェクトの root に [wrangler.toml/wrangler.json](https://developers.cloudflare.com/workers/platform/sites/configuration) ファイルを置くことを想定しています。内容としては以下のようなものです: ```toml /// file: wrangler.toml name = "" account_id = "" main = "./.cloudflare/worker.js" site.bucket = "./.cloudflare/public" build.command = "npm run build" compatibility_date = "2021-11-12" workers_dev = true ``` `` は何でも構いません。`` は、[Cloudflare dashboard](https://dash.cloudflare.com) にログインし、URL の末尾から取得できます: ``` https://dash.cloudflare.com/ ``` > [!NOTE] `.cloudflare` ディレクトリ (または `main` と `site.bucket` に指定したディレクトリ) を `.gitignore` に追加する必要があります。 [wrangler](https://developers.cloudflare.com/workers/wrangler/get-started/) をインストールしてログインする必要がありますが、もしまだやっていなければ: ``` npm i -g wrangler wrangler login ``` その後、アプリをビルドしデプロイすることができます: ```sh wrangler deploy ``` ## カスタム設定 If you would like to use a config file other than `wrangler.toml` you can specify so using the [`config` option](#Options-config). [Node.js 互換](https://developers.cloudflare.com/workers/runtime-apis/nodejs/#enable-nodejs-from-the-cloudflare-dashboard) を有効化したい場合は、`wrangler.toml` で "nodejs_compat" フラグを追加してください: ```toml /// file: wrangler.toml compatibility_flags = [ "nodejs_compat" ] ``` ## Runtime APIs [`env`](https://developers.cloudflare.com/workers/runtime-apis/fetch-event#parameters) オブジェクトにはあなたのプロジェクトの [bindings](https://developers.cloudflare.com/pages/platform/functions/bindings/) が含まれており、KV/DO namespaces などで構成されています。これは `platform` プロパティを介して [`context`](https://developers.cloudflare.com/workers/runtime-apis/handlers/fetch/#contextwaituntil)、[`caches`](https://developers.cloudflare.com/workers/runtime-apis/cache/)、[`cf`](https://developers.cloudflare.com/workers/runtime-apis/request/#the-cf-property-requestinitcfproperties) と一緒に SvelteKit に渡されます。つまり、hooks とエンドポイントでこれらにアクセスできるということです: ```js // @errors: 7031 export async function POST({ request, platform }) { const x = platform.env.YOUR_DURABLE_OBJECT_NAMESPACE.idFromName('x'); } ``` > [!NOTE] 環境変数については、SvelteKit の組み込みの `$env` モジュールの使用を推奨します。 これらの型をアプリで使用できるようにするには、`@cloudflare/workers-types` をインストールし、`src/app.d.ts` でこれらを参照します: ```ts /// file: src/app.d.ts +++import { KVNamespace, DurableObjectNamespace } from '@cloudflare/workers-types';+++ declare global { namespace App { interface Platform { +++ env?: { YOUR_KV_NAMESPACE: KVNamespace; YOUR_DURABLE_OBJECT_NAMESPACE: DurableObjectNamespace; };+++ } } } export {}; ``` ### Testing Locally Cloudflare Workers specific values in the `platform` property are emulated during dev and preview modes. Local [bindings](https://developers.cloudflare.com/workers/wrangler/configuration/#bindings) are created based on the configuration in your `wrangler.toml` file and are used to populate `platform.env` during development and preview. Use the adapter config [`platformProxy` option](#Options-platformProxy) to change your preferences for the bindings. For testing the build, you should use [wrangler](https://developers.cloudflare.com/workers/cli-wrangler) **version 3**. Once you have built your site, run `wrangler dev`. ## トラブルシューティング ### Worker size limits Workers にデプロイする場合、SvelteKit が生成したサーバーは1つのファイルにバンドルされます。minify 後に Worker が [そのサイズの上限](https://developers.cloudflare.com/workers/platform/limits/#worker-size) を超過する場合、Wrangler が Worker の公開に失敗します。通常、この制限に引っかかることはほとんどありませんが、一部の大きいライブラリではこれが発生することがあります。その場合、大きいライブラリをクライアントサイドでのみインポートするようにすることで、Worker のサイズを小さくすることができます。詳細は [FAQ](./faq#How-do-I-use-X-with-SvelteKit-How-do-I-use-a-client-side-only-library-that-depends-on-document-or-window) をご覧ください。 ### ファイルシステムにアクセスする Cloudflare Workers では `fs` を使用することはできません。そうする必要があるルート(route)については[プリレンダリング](page-options#prerender)する必要があります。 # Netlify Netlify にデプロイする場合は、[`adapter-netlify`](https://github.com/sveltejs/kit/tree/main/packages/adapter-netlify) を使用します。 [`adapter-auto`](adapter-auto) を使用している場合、この adapter は自動でインストールされますが、この adapter 自体をプロジェクトに追加すれば Netlify 固有のオプションを指定できるようになります。 ## 使い方 `npm i -D @sveltejs/adapter-netlify` を実行してインストールし、`svelte.config.js` にこの adapter を追加します: ```js // @errors: 2307 /// file: svelte.config.js import adapter from '@sveltejs/adapter-netlify'; export default { kit: { // default options are shown adapter: adapter({ // if true, will create a Netlify Edge Function rather // than using standard Node-based functions edge: false, // if true, will split your app into multiple functions // instead of creating a single one for the entire app. // if `edge` is true, this option cannot be used split: false }) } }; ``` そして、プロジェクトの root に [netlify.toml](https://docs.netlify.com/configure-builds/file-based-configuration) ファイルを置くのを忘れないでください。このファイルの `build.publish` に基づいて静的なアセットをどこに書き込むか決定します。こちらのサンプルの設定をご覧ください: ```toml [build] command = "npm run build" publish = "build" ``` `netlify.toml` ファイルが見つからない、もしくは `build.publish` の値が見つからない場合、`"build"` のデフォルト値が使用されます。Netlify の UI で publish ディレクトリを他の場所に設定する場合は、`netlify.toml` にも同じ場所を設定するか、`"build"` のデフォルト値を使用する必要があることにご注意ください。 ### Node version 新しいプロジェクトではデフォルトで現時点の Node LTS バージョンが使用されます。しかし、少し前に作成したプロジェクトをアップグレードする場合、古いバージョンで止まってしまうかもしれません。手動で現時点の Node バージョンを指定する場合、詳細は [Netlify のドキュメント](https://docs.netlify.com/configure-builds/manage-dependencies/#node-js-and-javascript)をご参照ください。 ## Netlify Edge Functions SvelteKit は [Netlify Edge Functions](https://docs.netlify.com/netlify-labs/experimental-features/edge-functions/) をサポートしています。`adapter` 関数に `edge: true` オプションを渡すと、サイト訪問者に近い場所にデプロイされる Deno ベースの edge function でサーバーサイドレンダリングが行われるようになります。`false` を設定した場合 (デフォルト)、サイトは Node ベースの Netlify Functions にデプロイされます。 ```js // @errors: 2307 /// file: svelte.config.js import adapter from '@sveltejs/adapter-netlify'; export default { kit: { adapter: adapter({ // will create a Netlify Edge Function using Deno-based // rather than using standard Node-based functions edge: true }) } }; ``` ## SvelteKit の機能を代替する Netlify の機能 Netlify の機能に依存することなく、SvelteKit が直接提供する機能を使ってアプリを構築することができます。こういった機能は SvelteKit のほうを選択すると、開発モードでその機能を使用でき、インテグレーションテストが可能になり、Netlify から切り替えることになった場合に他の adapter で動作させることができます。しかし、シナリオによっては Netlify のほうの機能を使用したほうが有益な場合もあります。例えば、すでに Netlify でホストされているアプリを SvelteKit に移行する場合です。 ### リダイレクトルール(Redirect rules) コンパイル時に、リダイレクトルールは自動で `_redirects` ファイルに追記されます (もし存在しない場合は、作成されます)。つまり: - `_redirects` のほうが[優先度が高い](https://docs.netlify.com/routing/redirects/#rule-processing-order)ため、`netlify.toml` にある `[[redirects]]` には決してマッチしません。そのため、ルールは常に [`_redirects` ファイル](https://docs.netlify.com/routing/redirects/#syntax-for-the-redirects-file)に記載してください。 - `_redirects` には、`/* /foobar/:splat` のようなカスタムの "catch all" ルールを置くべきではありません。そうしないと、自動で追加されたルールが適用されなくなります。Netlify は[最初にマッチしたルール](https://docs.netlify.com/routing/redirects/#rule-processing-order)だけを処理するからです。 ### Netlify Forms 1. [こちら](https://docs.netlify.com/forms/setup/#html-forms)にあるように、例えば `/routes/contact/+page.svelte` に、Netlify HTML form を作成します。(hidden の `form-name` input 要素を追加するのを忘れずに!) 2. Netlify の build bot はデプロイ時にあなたの HTML ファイルをパースします。つまり、あなたの form は HTML として[プリレンダリング](page-options#prerender)されるようにしておかないといけません。あなたの `contact.svelte` に `export const prerender = true` を追加してそのページだけプリレンダリングするか、または `kit.prerender.force: true` オプションを設定して全てのページをプリレンダリングするようにしておくか、で対応できます。 3. あなたの Netlify form に `` のような[カスタムの成功メッセージ](https://docs.netlify.com/forms/setup/#success-messages)がある場合、それに対応する `/routes/success/+page.svelte` が存在しプリレンダリングされるか確認してください。 ### Netlify Functions この adapter によって、SvelteKit エンドポイントは [Netlify Functions](https://docs.netlify.com/functions/overview/) としてホストされます。Netlify function ハンドラには追加のコンテキストがあり、[Netlify Identity](https://docs.netlify.com/visitor-access/identity/) 情報が含まれています。このコンテキストは、あなたの hooks や `+page.server` と `+layout.server` エンドポイント の中で `event.platform.context` フィールドを介してアクセスできます。adapter config の `edge` プロパティが `false` の場合は[serverless functions](https://docs.netlify.com/functions/overview/)、`true` の場合は [edge functions](https://docs.netlify.com/edge-functions/overview/#app) となります。 ```js // @errors: 2705 7006 /// file: +page.server.js export const load = async (event) => { const context = event.platform.context; console.log(context); // shows up in your functions log in the Netlify app }; ``` さらに、ディレクトリを追加して `netlify.toml` に設定を追加することで、独自の Netlify functions を追加することができます。例えば: ```toml [build] command = "npm run build" publish = "build" [functions] directory = "functions" ``` ## トラブルシューティング ### ファイルシステムにアクセスする edge デプロイメントでは `fs` を使用することはできません。 serverless デプロイメントでは `fs` を使用できますが、ファイルがプロジェクトからデプロイメントにコピーされないため、期待通りには動作しないでしょう。代わりに `$app/server` の [`read`]($app-server#read) 関数を使用してファイルにアクセスしてください。edge デプロイメントでは `read` は動作しません(将来的に変更される可能性があります)。 その代わりに、`fs` を使用する必要があるルート(route)については[プリレンダリング](page-options#prerender)する必要があります。 # Vercel Vercel にデプロイする場合は、[`adapter-vercel`](https://github.com/sveltejs/kit/tree/main/packages/adapter-vercel) を使用します。 [`adapter-auto`](adapter-auto) を使用している場合、この adapter は自動でインストールされますが、この adapter 自体をプロジェクトに追加すれば Vercel 固有のオプションを指定できるようになります。 ## 使い方 `npm i -D @sveltejs/adapter-vercel` を実行してインストールし、`svelte.config.js` にこの adapter を追加します: ```js // @errors: 2307 2345 /// file: svelte.config.js import adapter from '@sveltejs/adapter-vercel'; export default { kit: { adapter: adapter({ // ここで設定できるオプションについては以下を参照 }) } }; ``` ## デプロイメントの設定 Vercel にルート(routes)を function としてデプロイする方法をコントロールするには、デプロイメントの設定を、上記に示すオプションか、`+server.js`、`+page(.server).js`、`+layout(.server).js` ファイルの中の [`export const config`](page-options#config) を使用して、行うことができます。 例えば、アプリの一部を [Edge Functions](https://vercel.com/docs/concepts/functions/edge-functions) としてデプロイして… ```js /// file: about/+page.js /** @type {import('@sveltejs/adapter-vercel').Config} */ export const config = { runtime: 'edge' }; ``` …他の部分を [Serverless Functions](https://vercel.com/docs/concepts/functions/serverless-functions) としてデプロイすることができます (layout の内側の `config` は、すべての子ページに適用されます): ```js /// file: admin/+layout.js /** @type {import('@sveltejs/adapter-vercel').Config} */ export const config = { runtime: 'nodejs22.x' }; ``` 以下のオプションはすべての function に適用されます: - `runtime`: `'edge'`、`'nodejs18.x'`、`'nodejs20.x'`、`'nodejs22.x'`。デフォルトでは、adapter はプロジェクトの Node のバージョンに対応した `'nodejs.x'` を選択します。プロジェクトの Node バージョンは Vercel のダッシュボードから設定することができます。 - `regions`: [edge network regions](https://vercel.com/docs/concepts/edge-network/regions) の配列 (serverless functions のデフォルトは `["iad1"]`) か、`runtime` が `edge` (デフォルト) の場合は `'all'` です。serverless functions の場合の複数の regions のサポートは Enterprise Plan のみです。 - `split`: `true` の場合、ルート(route)は個別の function としてデプロイされます。`split` を adapter レベルで `true` にする場合、すべてのルート(route)が個別の function としてデプロイされます。 加えて、以下のオプションは edge function に適用されます: - `external`: esbuild が function をバンドルする際に外部(external)として扱う依存関係(dependencies)の配列です。Node の外側で実行されないオプションの依存関係(optional dependencies)を除外したいときにのみ使用してください そして以下のオプションは serverless function に適用されます: - `memory`: function で利用できるメモリ量です。デフォルトは `1024` Mb で、`128` Mb まで減らすことができます。また、Pro または Enterprise アカウントの場合は、`3008` Mb まで[増やす](https://vercel.com/docs/concepts/limits/overview#serverless-function-memory)ことができます。間隔は 64Mb 単位です。 - `maxDuration`: function の [最大実行時間(maximum execution duration)](https://vercel.com/docs/functions/runtimes#max-duration)。デフォルトで、Hobby アカウントの場合は `10` 秒、Pro の場合は `15`、Enterprise の場合は `900` です。 - `isr`: Incremental Static Regeneration の設定、詳細は後述 function から特定の region のデータにアクセスする必要がある場合は、パフォーマンスを最適化するためそれと同じ region (またはその知覚) にデプロイすることをおすすめします。 ## Image Optimization You may set the `images` config to control how Vercel builds your images. See the [image configuration reference](https://vercel.com/docs/build-output-api/v3/configuration#images) for full details. As an example, you may set: ```js // @errors: 2300 2842 7031 1181 1005 1136 1128 /// file: svelte.config.js import adapter from '@sveltejs/adapter-vercel'; export default { kit: { adapter({ images: { sizes: [640, 828, 1200, 1920, 3840], formats: ['image/avif', 'image/webp'], minimumCacheTTL: 300, domains: ['example-app.vercel.app'], } }) } }; ``` ## Incremental Static Regeneration Vercel は [Incremental Static Regeneration](https://vercel.com/docs/incremental-static-regeneration) (ISR) をサポートしており、これにより、プリレンダリングコンテンツが持つパフォーマンスとコストの利点と、ダイナミックレンダリングコンテンツが持つ柔軟性の両方を提供することができます。 > Use ISR only on routes where every visitor should see the same content (much like when you prerender). If there's anything user-specific happening (like session cookies), they should happen on the client via JavaScript only to not leak sensitive information across visits ISR をルート(route)に追加するには、`config` オブジェクトに `isr` プロパティを含めます: ```js // @errors: 2664 import { BYPASS_TOKEN } from '$env/static/private'; export const config = { isr: { expiration: 60, bypassToken: BYPASS_TOKEN, allowQuery: ['search'] } }; ``` > Using ISR on a route with `export const prerender = true` will have no effect, since the route is prerendered at build time The `expiration` property is required; all others are optional. The properties are discussed in more detail below. ### expiration The expiration time (in seconds) before the cached asset will be re-generated by invoking the Serverless Function. Setting the value to `false` means it will never expire. In that case, you likely want to define a bypass token to re-generate on demand. ### bypassToken A random token that can be provided in the URL to bypass the cached version of the asset, by requesting the asset with a `__prerender_bypass=` cookie. Making a `GET` or `HEAD` request with `x-prerender-revalidate: ` will force the asset to be re-validated. Note that the `BYPASS_TOKEN` string must be at least 32 characters long. You could generate one using the JavaScript console like so: ```console btoa(Math.random().toString()).substring(0,32); ``` Set this string as an environment variable on Vercel by logging in and going to your project then Settings > Environment Variables. For "Key" put `BYPASS_TOKEN` and for "value" use the string generated above, then hit "Save". To get this key known about for local development, you can use the [Vercel CLI](https://vercel.com/docs/cli/env) by running the `vercel env pull` command locally like so: ```console $ vercel env pull .env.development.local ``` ### allowQuery A list of valid query parameters that contribute to the cache key. Other parameters (such as utm tracking codes) will be ignored, ensuring that they do not result in content being re-generated unnecessarily. By default, query parameters are ignored. > Pages that are [prerendered](page-options#prerender) will ignore ISR configuration. ## 環境変数 Vercel では[デプロイメント固有の環境変数](https://vercel.com/docs/concepts/projects/environment-variables#system-environment-variables)一式を使用できます。他の環境変数と同様、`$env/static/private` と `$env/dynamic/private` からアクセスでき (詳細は後述)、public のほうからはアクセスできません。クライアントからこれらの変数にアクセスするには: ```js // @errors: 2305 /// file: +layout.server.js import { VERCEL_COMMIT_REF } from '$env/static/private'; /** @type {import('./$types').LayoutServerLoad} */ export function load() { return { deploymentGitBranch: VERCEL_COMMIT_REF }; } ``` ```svelte

This staging environment was deployed from {data.deploymentGitBranch}.

``` Vercel でビルドする場合、これらの変数は全てビルド時と実行時で変わらないため、`$env/dynamic/private` ではなく、変数を静的に置換しデッドコードの削除などの最適化ができる `$env/static/private` の使用をおすすめします。 ## Skew protection When a new version of your app is deployed, assets belonging to the previous version may no longer be accessible. If a user is actively using your app when this happens, it can cause errors when they navigate — this is known as _version skew_. SvelteKit mitigates this by detecting errors resulting from version skew and causing a hard reload to get the latest version of the app, but this will cause any client-side state to be lost. (You can also proactively mitigate it by observing the [`updated`]($app-stores#updated) store value, which tells clients when a new version has been deployed.) [Skew protection](https://vercel.com/docs/deployments/skew-protection) is a Vercel feature that routes client requests to their original deployment. When a user visits your app, a cookie is set with the deployment ID, and any subsequent requests will be routed to that deployment for as long as skew protection is active. When they reload the page, they will get the newest deployment. (The `updated` store is exempted from this behaviour, and so will continue to report new deployments.) To enable it, visit the Advanced section of your project settings on Vercel. Cookie-based skew protection comes with one caveat: if a user has multiple versions of your app open in multiple tabs, requests from older versions will be routed to the newer one, meaning they will fall back to SvelteKit's built-in skew protection. ## Notes ### Vercel functions プロジェクトの root の `api` ディレクトリに Vercel functions がある場合、`/api/*` に対するリクエストは SvelteKit で処理されません。Vercel functions に JavaScript 以外の言語を使用する必要が無いのであれば、SvelteKit アプリの [API ルート(routes)](routing#server) として実装すると良いでしょう。逆に Vercel functions に JavaScript 以外の言語を使用する必要がある場合は、SvelteKit アプリに `/api/*` ルート(routes)を含めないようにしてください。 ### Node version ある時期より前に作成されたプロジェクトは、SvelteKit に必要な Node バージョンより古い Node バージョンを使用しているかもしれません。[プロジェクトの設定で Node のバージョンを変更する](https://vercel.com/docs/concepts/functions/serverless-functions/runtimes/node-js#node.js-version)ことができます。 ## トラブルシューティング ### ファイルシステムにアクセスする edge functions では `fs` を使用することはできません。 serverless functions では `fs` を使用できますが、ファイルがプロジェクトからデプロイメントにコピーされないため、期待通りには動作しないでしょう。代わりに `$app/server` の [`read`]($app-server#read) 関数を使用してファイルにアクセスしてください。edge functions にデプロイされたルート(route)では `read` は動作しません(将来的に変更される可能性があります)。 その代わりに、`fs` を使用する必要があるルート(route)については[プリレンダリング](page-options#prerender)する必要があります。 # adapter を書く あなたが使いたい環境向けの adapter がまだ存在しない場合は、ご自身で adapter を作成することができます。あなたが使いたい環境に似ているプラットフォームの [adapter のソースを見て](https://github.com/sveltejs/kit/tree/main/packages)、コピーするところから始めることをおすすめします。 Adapter パッケージは以下の API を実装しなければなりません。これによって `Adapter` が作られます: ```js // @errors: 2322 // @filename: ambient.d.ts type AdapterSpecificOptions = any; // @filename: index.js // ---cut--- /** @param {AdapterSpecificOptions} options */ export default function (options) { /** @type {import('@sveltejs/kit').Adapter} */ const adapter = { name: 'adapter-package-name', async adapt(builder) { // adapter implementation }, async emulate() { return { async platform({ config, prerender }) { // the returned object becomes `event.platform` during dev, build and // preview. Its shape is that of `App.Platform` } } }, supports: { read: ({ config, route }) => { // Return `true` if the route with the given `config` can use `read` // from `$app/server` in production, return `false` if it can't. // Or throw a descriptive error describing how to configure the deployment } } }; return adapter; } ``` このうち、`name` と `adapt` は必須です。`emulate` と `supports` はオプションです。 `adapt` メソッドの中で、adapter が行うべきことがいくつかあります: - build ディレクトリの掃除 - SvelteKit の出力を `builder.writeClient`、`builder.writeServer`、`builder.writePrerendered` で書き出す - これらのコードを出力する: - `${builder.getServerDirectory()}/index.js` から `Server` をインポートする - `builder.generateManifest({ relativePath })` で生成された manifest でアプリをインスタンス化する - 必要に応じて、プラットフォームからのリクエストをリスン(Listen)しそのリクエストを標準の [Request](https://developer.mozilla.org/ja/docs/Web/API/Request) に変換し、`server.respond(request, { getClientAddress })` 関数を呼び出して [Response](https://developer.mozilla.org/ja/docs/Web/API/Response) を生成して応答する - `server.respond` に渡される `platform` オプションを使用してプラットフォーム固有の情報を SvelteKit に公開する - 必要に応じて、ターゲットのプラットフォームで動作するよう `fetch` をグローバルにシム(shim)する。SvelteKit は、プラットフォームが `undici` を使用できるようにするための `@sveltejs/kit/node/polyfills` ヘルパーを提供しています - 必要に応じて、ターゲットのプラットフォームで依存関係(dependencies)をインストールするのを避けるため、出力をバンドルする - ユーザーの静的ファイルと生成された JS/CSS をターゲットのプラットフォームにとって適切な場所に配置する 可能であれば、adapter の出力は `build/` ディレクトリに置き、中間出力は `.svelte-kit/[adapter-name]` に置くことをおすすめします。 # 高度なルーティング ## Restパラメータ ルートセグメント(route segments)の数がわからない場合は、rest 構文を使用することができます。例えば GitHub のファイルビューアのようなものを実装する場合は… ```bash /[org]/[repo]/tree/[branch]/[...file] ``` …この場合、`/sveltejs/kit/tree/main/documentation/docs/04-advanced-routing.md` をリクエストすると、以下のパラメータをページで使うことができます: ```js // @noErrors { org: 'sveltejs', repo: 'kit', branch: 'main', file: 'documentation/docs/04-advanced-routing.md' } ``` > [!NOTE] `src/routes/a/[...rest]/z/+page.svelte` は `/a/z` にも (つまり、パラメータが全くない場合にも)、`/a/b/z` や `/a/b/c/z` と同様にマッチします。Rest パラメータの値が正しいことを、例えば [matcher](#Matching) を使用するなどして確認してください。 ### 404 pages Rest パラメータによってカスタムの 404 をレンダリングすることができます。これらのルート(routes)があるとして… ```tree src/routes/ ├ marx-brothers/ │ ├ chico/ │ ├ harpo/ │ ├ groucho/ │ └ +error.svelte └ +error.svelte ``` …もし `/marx-brothers/karl` にリクエストしても、`marx-brothers/+error.svelte` ファイルはレンダリング _されません_ 。なぜならどのルート(route) にもマッチしないからです。もしネストしたエラーページをレンダリングしたければ、どんな `/marx-brothers/*` リクエストにもマッチするルート(route)を作成し、そこから 404 を返すようにしてください: ```tree src/routes/ ├ marx-brothers/ +++| ├ [...path]/+++ │ ├ chico/ │ ├ harpo/ │ ├ groucho/ │ └ +error.svelte └ +error.svelte ``` ```js /// file: src/routes/marx-brothers/[...path]/+page.js import { error } from '@sveltejs/kit'; /** @type {import('./$types').PageLoad} */ export function load(event) { error(404, 'Not Found'); } ``` > [!NOTE] もし 404 のケースをハンドリングしていない場合、[`handleError`](hooks#Shared-hooks-handleError) によって表示が行われます。 ## Optional parameters `[lang]/home` というルートに含まれる `lang` というパラメータは必須です。これらのパラメータをオプションにできると、今回の例では `home` と `en/home` のどちらも同じページを指すことができるのでとても便利です。パラメータにもう1つ括弧を付けることでこれができるようになります: `[[lang]]/home` optional のルートパラメータ(route parameter)は rest パラメータに続けて使用すること (`[...rest]/[[optional]]`) はできません。パラメータは 'greedily' にマッチし、optional のパラメータは使用されないこともあるためです。 ## Matching `src/routes/fruits/[page]` というルート(route)は `/fruits/apple` にマッチしますが、`/fruits/rocketship` にもマッチしてしまいます。これを防ぎたい場合、パラメータ文字列(`"apple"` や `"rocketship"`)を引数に取ってそれが有効なら `true` を返す _matcher_ を [`params`](configuration#files) ディレクトリに追加することで、ルート(route)のパラメータを適切に定義することができます… ```js /// file: src/params/fruit.js /** * @param {string} param * @return {param is ('apple' | 'orange')} * @satisfies {import('@sveltejs/kit').ParamMatcher} */ export function match(param) { return param === 'apple' || param === 'orange'; } ``` …そしてルート(routes)を拡張します: ``` src/routes/fruits/[page+++=fruit+++] ``` もしパス名がマッチしない場合、SvelteKit は (後述のソート順の指定に従って) 他のルートでマッチするか試行し、どれにもマッチしない場合は最終的に 404 を返します。 `params` ディレクトリにある各モジュールは matcher に対応しています。ただし、matcher のユニットテストに使用される `*.test.js` と `*.spec.js` ファイルは例外です。 > [!NOTE] Matcher は サーバーとブラウザの両方で動作します。 ## ソート(Sorting) あるパスに対し、マッチするルート(routes)は複数でも構いません。例えば、これらのルート(routes)はどれも `/foo-abc` にマッチします: ```bash src/routes/[...catchall]/+page.svelte src/routes/[[a=x]]/+page.svelte src/routes/[b]/+page.svelte src/routes/foo-[c]/+page.svelte src/routes/foo-abc/+page.svelte ``` SvelteKit は、どのルート(route)に対してリクエストされているのかを判断しなければなりません。そのため、以下のルールに従ってこれらをソートします… - より詳細・明確(specific)なルート(routes)ほど、より優先度が高い (例えば、動的なパラメータが1つあるルートより、パラメータのないルートのほうがより詳細・明確(specific)である、など) - [matchers](#Matching) 付きのパラメータ (`[name=type]`) は matchers なしのパラメータ (`[name]`) よりも優先度が高い - `[[optional]]` と `[...rest]` パラメータはルート(route)の最後の部分でない限り無視される (最後の部分になっている場合は最も低い優先度として扱われる)。言い換えると、ソートの目的上、`x/[[y]]/z` と `x/z` は同等に扱われる - 優先度が同じ場合はアルファベット順で解決される …この順序で並べると、`/foo-abc` の場合は `src/routes/foo-abc/+page.svelte` を呼び出し、`/foo-def` の場合は `src/routes/foo-[c]/+page.svelte` を呼び出します: ```bash src/routes/foo-abc/+page.svelte src/routes/foo-[c]/+page.svelte src/routes/[[a=x]]/+page.svelte src/routes/[b]/+page.svelte src/routes/[...catchall]/+page.svelte ``` ## エンコード(Encoding) ファイルシステムでは使用できない文字があります — Linux と Mac では `/`、Windows では `\ / : * ? " < > |` です。URL においては、`#` と `%` には特別な意味がありますし、SvelteKit においては `[ ] ( )` に特別な意味があります。そのため、これらの文字をそのままルート(route)に使用することはできません。 これらの文字をルート(route)に使用するには、16進数のエスケープシーケンスを使います。`[x+nn]` というフォーマットで、`nn` の部分は16進数の文字コードです: - `\` — `[x+5c]` - `/` — `[x+2f]` - `:` — `[x+3a]` - `*` — `[x+2a]` - `?` — `[x+3f]` - `"` — `[x+22]` - `<` — `[x+3c]` - `>` — `[x+3e]` - `|` — `[x+7c]` - `#` — `[x+23]` - `%` — `[x+25]` - `[` — `[x+5b]` - `]` — `[x+5d]` - `(` — `[x+28]` - `)` — `[x+29]` 例えば、`/smileys/:-)` というルート(route)を作る場合は、`src/routes/smileys/[x+3a]-[x+29]/+page.svelte` ファイルを作成します。 JavaScript を使って文字の16進数コードを判定することができます: ```js ':'.charCodeAt(0).toString(16); // '3a', hence '[x+3a]' ``` また、Unicode のエスケープシーケンスを使用することもできます。通常、エンコードされていない文字を直接使用することができるので、こうする必要はありませんが、何らかの理由で、例えばファイル名に絵文字を使用することができない場合、エスケープ文字を使用することができます。言い換えると、以下は同じことをしているということです: ``` src/routes/[u+d83e][u+dd2a]/+page.svelte src/routes/🤪/+page.svelte ``` Unicode エスケープシーケンスのフォーマットは `[u+nnnn]` で、`nnnn` の部分は `0000` から `10ffff` までの適切な値です (JavaScript の文字列エスケープとは異なり、`ffff` 以上のコードポイントを表現するためにサロゲートペアを使用する必要はありません)。Unicode エンコーディングについてもっと知りたい方は、[Programming with Unicode](https://unicodebook.readthedocs.io/unicode_encodings.html) を参照してください。 > [!NOTE] ディレクトリの先頭に `.` 文字があると、TypeScript で [問題](https://github.com/microsoft/TypeScript/issues/13399) が起きるため、例えば [`.well-known`](https://en.wikipedia.org/wiki/Well-known_URI) のようなルート(route)を作る場合はこれらの文字をエンコードしておくと良いでしょう: `src/routes/[x+2e]well-known/...` ## Advanced layouts デフォルトでは、 _レイアウトの階層_ が _ルート(route)の階層_ に反映されます。場合によっては、そうしたくないこともあるかもしれません。 ### (group) 'アプリ' のルート(routes)としてのレイアウト (例えば `/dashboard` や `/item`) が1つあり、'マーケティング' のルート(routes)としての別のレイアウト (`/about` や `/testimonials`) があるかもしれません。これらのルート(routes)を、ディレクトリの名前を括弧でくくることでグループ化することができます。通常のディレクトリとは異なり、`(app)` や `(marketing)` はそれらの中のルート(routes)の URL パス名には影響しません: ```tree src/routes/ +++│ (app)/+++ │ ├ dashboard/ │ ├ item/ │ └ +layout.svelte +++│ (marketing)/+++ │ ├ about/ │ ├ testimonials/ │ └ +layout.svelte ├ admin/ └ +layout.svelte ``` `+page` を `(group)` の中に直接配置することもできます (例えば、`/` が `(app)` や `(marketing)` のページであるべき場合など)。 ### Breaking out of layouts 最上位のレイアウト(root layout)は、アプリの全てのページに適用されます。省略した場合、デフォルトは `{@render children()}` です。もし、いくつかのページで他のページとは異なるレイアウト階層を持ちたい場合には、アプリ全体を1つまたは複数のグループにして、共通のレイアウトを継承しないルート(route)を分けることができます。 上記の例で、`/admin` ルート(route)は `(app)` や `(marketing)` のレイアウトを継承しません。 ### +page@ ページは、ルート(route)ごとに現在のレイアウト階層から抜け出すことができます。先ほどの例に出てきた `(app)` グループの中に、`/item/[id]/embed` ルート(route)があるとします: ```tree src/routes/ ├ (app)/ │ ├ item/ │ │ ├ [id]/ │ │ │ ├ embed/ +++│ │ │ │ └ +page.svelte+++ │ │ │ └ +layout.svelte │ │ └ +layout.svelte │ └ +layout.svelte └ +layout.svelte ``` 通常、これは最上位のレイアウト(root layout)と `(app)` レイアウトと `item` レイアウトと `[id]` レイアウトを継承します。`@` と、その後ろにセグメント名 (最上位のレイアウト(root layout)の場合は空文字列(empty string)) を追加することで、これらのレイアウトのどれかにリセットすることができます。この例では、以下のオプションから選択できます: - `+page@[id].svelte` - `src/routes/(app)/item/[id]/+layout.svelte` を継承します - `+page@item.svelte` - `src/routes/(app)/item/+layout.svelte` を継承します - `+page@(app).svelte` - `src/routes/(app)/+layout.svelte` を継承します - `+page@.svelte` - `src/routes/+layout.svelte` を継承します ```tree src/routes/ ├ (app)/ │ ├ item/ │ │ ├ [id]/ │ │ │ ├ embed/ +++│ │ │ │ └ +page@(app).svelte+++ │ │ │ └ +layout.svelte │ │ └ +layout.svelte │ └ +layout.svelte └ +layout.svelte ``` ### +layout@ ページと同じように、同じ方法でレイアウト _自体_ をその親のレイアウトの階層から外すことができます。例えば、`+layout@.svelte` コンポーネントはその全ての子ルート(routes)の階層をリセットします。 ``` src/routes/ ├ (app)/ │ ├ item/ │ │ ├ [id]/ │ │ │ ├ embed/ │ │ │ │ └ +page.svelte // uses (app)/item/[id]/+layout.svelte │ │ │ ├ +layout.svelte // inherits from (app)/item/+layout@.svelte │ │ │ └ +page.svelte // uses (app)/item/+layout@.svelte │ │ └ +layout@.svelte // inherits from root layout, skipping (app)/+layout.svelte │ └ +layout.svelte └ +layout.svelte ``` ### レイアウトグループを使うときは 全てのユースケースがレイアウトのグループ化に適しているわけではありませんし、無理に使用する必要もありません。あなたのユースケースが複雑な `(group)` のネストになってしまうかもしれませんし、たった1つの例外ケースのために `(group)` を導入したくないかもしれません。コンポジション (再利用可能な `load` 関数や Svelte コンポーネント) や if 文など、他の手段を使用してやりたいことを実現するのは全く問題ありません。以下の例では、最上位のレイアウト(root layout)に戻し、他のレイアウトでも使用できるコンポーネントや関数を再利用したレイアウトを示しています: ```svelte {@render children()} ``` ```js /// file: src/routes/nested/route/+layout.js // @filename: ambient.d.ts declare module "$lib/reusable-load-function" { export function reusableLoad(event: import('@sveltejs/kit').LoadEvent): Promise>; } // @filename: index.js // ---cut--- import { reusableLoad } from '$lib/reusable-load-function'; /** @type {import('./$types').PageLoad} */ export function load(event) { // Add additional logic here, if needed return reusableLoad(event); } ``` ## その他の参考情報 - [Tutorial: Advanced Routing](/tutorial/kit/optional-params) # Hooks 'Hooks' は、特定のイベントに対して SvelteKit がレスポンスを呼び出すことを宣言するアプリ全体の関数で、これによってフレームワークの動作をきめ細やかに制御できるようになります。 hooks ファイルは2つあり、どちらもオプションです: - `src/hooks.server.js` — アプリのサーバーの hooks - `src/hooks.client.js` — アプリのクライアントの hooks - `src/hooks.js` — サーバーとクライアントの両方で実行される hooks これらのモジュールのコードはアプリケーションの起動時に実行されるので、データベースクライアントの初期化などに有用です。 > [!NOTE] これらのファイルの場所は [`config.kit.files.hooks`](configuration#files) で設定できます。 ## Server hooks 以下の hooks は `src/hooks.server.js` に追加することができます: ### handle この関数は SvelteKit のサーバーが [リクエスト](web-standards#Fetch-APIs-Request) を受けるたびに (アプリの実行中であろうと、[プリレンダリング](page-options#prerender)であろうと) 実行され、[レスポンス](web-standards#Fetch-APIs-Response) を決定します。リクエストを表す `event` オブジェクトと、ルート(route)をレンダリングしレスポンスを生成する `resolve` という関数を受け取ります。これにより、レスポンスのヘッダーやボディを変更したり、SvelteKitを完全にバイパスすることができます (例えば、プログラムでルート(routes)を実装する場合など)。 ```js /// file: src/hooks.server.js /** @type {import('@sveltejs/kit').Handle} */ export async function handle({ event, resolve }) { if (event.url.pathname.startsWith('/custom')) { return new Response('custom response'); } const response = await resolve(event); return response; } ``` > [!NOTE] 静的アセット(プリレンダリング済みのページを含む)に対するリクエストは SvelteKit では処理されません。 未実装の場合、デフォルトは `({ event, resolve }) => resolve(event)` となります。 During prerendering, SvelteKit crawls your pages for links and renders each route it finds. Rendering the route invokes the `handle` function (and all other route dependencies, like `load`). If you need to exclude some code from running during this phase, check that the app is not [`building`]($app-environment#building) beforehand. ### locals カスタムデータをリクエストに追加し、`+server.js` のハンドラや server `load` 関数に渡すには、以下のように `event.locals` オブジェクトに埋め込んでください。 ```js /// file: src/hooks.server.js // @filename: ambient.d.ts type User = { name: string; } declare namespace App { interface Locals { user: User; } } const getUserInformation: (cookie: string | void) => Promise; // @filename: index.js // ---cut--- /** @type {import('@sveltejs/kit').Handle} */ export async function handle({ event, resolve }) { event.locals.user = await getUserInformation(event.cookies.get('sessionid')); const response = await resolve(event); response.headers.set('x-custom-header', 'potato'); return response; } ``` [`sequence` ヘルパー関数](@sveltejs-kit-hooks)を使用すると、複数の `handle` 関数を定義することができます。 `resolve` はオプションの第2引数をサポートしており、レスポンスのレンダリング方法をより詳細にコントロールすることができます。そのパラメータは、以下のフィールドを持つオブジェクトです: - `transformPageChunk(opts: { html: string, done: boolean }): MaybePromise` — カスタムの変換を HTML に適用します。`done` が true である場合、それは最後のチャンクです。チャンクが整形された HTML であることは保証されませんが (例えば、要素の開始タグは含むが終了タグは含まれない、など)、常に `%sveltekit.head%` やレイアウト(layout)/ページ(page)コンポーネントなどのような理にかなった境界 (sensible boundaries) で分割されます。 - `filterSerializedResponseHeaders(name: string, value: string): boolean` — `load` 関数が `fetch` でリソースを読み込むときに、シリアライズされるレスポンスにどのヘッダーを含めるかを決定します。デフォルトでは何も含まれません。 - `preload(input: { type: 'js' | 'css' | 'font' | 'asset', path: string }): boolean` — `` タグにどのファイルをプリロードの対象として追加するか決定します。このメソッドはビルド時、コードチャンクを構築している際に見つかったファイルごとに呼び出されます。これにより、例えば `+page.svelte` に `import './styles.css` がある場合、そのページに訪れたときにその CSS ファイルへの解決されたパスを以て `preload` が呼び出されるようになります。これはビルド時の分析によって行われるため、開発モードでは `preload` が呼ばれないことにご注意ください。プリロードによってその対象がより早くダウンロードされるようになるためパフォーマンスが改善しますが、不必要に多くのものをダウンロードしてしまうと、core web vitals を悪化させてしまいます。デフォルトでは、`js`、`css` ファイルがプリロードされます。現時点では `asset` ファイルはプリロードされませんが、フィードバックによっては追加されるかもしれません。 ```js /// file: src/hooks.server.js /** @type {import('@sveltejs/kit').Handle} */ export async function handle({ event, resolve }) { const response = await resolve(event, { transformPageChunk: ({ html }) => html.replace('old', 'new'), filterSerializedResponseHeaders: (name) => name.startsWith('x-'), preload: ({ type, path }) => type === 'js' || path.includes('/important/') }); return response; } ``` `resolve(...)` は決してエラーをスローせず、適切なステータスコードと `Promise` を返すことにご注意ください。もし `handle` 中に他の場所でエラーがスローされた場合、それは致命的(fatal)なものとして扱われ、SvelteKit は `Accept` ヘッダーに応じて、そのエラーの JSON 表現か、`src/error.html` でカスタマイズ可能なフォールバックエラーページをレスポンスとして返します。エラーハンドリングの詳細は [こちら](errors) からお読み頂けます。 ### handleFetch この関数は、サーバー上で (またはプリレンダリング中に) 実行される `load` 関数や `action` 関数の中で発生する `fetch` リクエストを変更 (または置換) することできます。 例えば、ユーザーがクライアントサイドでそれぞれのページに移動する際に、`load` 関数で `https://api.yourapp.com` のようなパブリックな URL にリクエストを行うかもしれませんが、SSR の場合には (パブリックなインターネットとの間にあるプロキシやロードバランサーをバイパスして) API を直接呼ぶほうが理にかなっているでしょう。 ```js /// file: src/hooks.server.js /** @type {import('@sveltejs/kit').HandleFetch} */ export async function handleFetch({ request, fetch }) { if (request.url.startsWith('https://api.yourapp.com/')) { // clone the original request, but change the URL request = new Request( request.url.replace('https://api.yourapp.com/', 'http://localhost:9999/'), request ); } return fetch(request); } ``` **Credentials** 同一オリジン(same-origin)リクエストの場合、SvelteKit の `fetch` 実装は、`credentials` オプションを `"omit"` にしない限り、 `cookie` と `authorization` ヘッダーを転送します。 クロスオリジン(cross-origin)リクエストの場合、リクエスト URL がアプリのサブドメインに属するときは `cookie` はリクエストに含まれます。例えば、あなたのアプリが `my-domain.com` にあり、あなたの API が `api.my-domain.com` にある場合、cookie はリクエストに含まれることになります。 もしあなたのアプリと API が兄弟関係にあるサブドメイン (例えば `www.my-domain.com` と `api.my-domain.com`) の場合は、`my-domain.com` のような共通の親ドメインに属する cookie は含まれません、なぜなら SvelteKit にはその cookie がどのドメインに属するか判断する方法がないからです。こういったケースでは、`handleFetch` を使って手動で cookie を含める必要があります: ```js /// file: src/hooks.server.js // @errors: 2345 /** @type {import('@sveltejs/kit').HandleFetch} */ export async function handleFetch({ event, request, fetch }) { if (request.url.startsWith('https://api.my-domain.com/')) { request.headers.set('cookie', event.request.headers.get('cookie')); } return fetch(request); } ``` ## Shared hooks 以下は `src/hooks.server.js` _と_ `src/hooks.client.js` のどちらにも追加できます: ### handleError [予期せぬエラー](errors#Unexpected-errors)がロード中またはレンダリング中にスローされると、この関数が `error`、`event`、`status` コード、`message` を引数にとって呼び出されます。これによって以下の2つのことが可能になります: - エラーをログに残すことができます - エラーからメッセージやスタックトレースなどの機密情報を省略し、ユーザーに見せても安全なカスタムの表現を生成することができます。戻り値のデフォルトは `{ message }` で、`$page.error` の値となります。 あなたのコード (またはあなたのコードから呼び出されたライブラリのコード) からスローされたエラーの場合、ステータスは 500 となり、message は "Internal Error" になります。`error.message` にはユーザーに公開されるべきではない機密情報が含まれている可能性がありますが、`message` は安全です (一般的なユーザーにとっては無意味ではありますが)。 `$page.error` オブジェクトに型安全な方法で情報を追加するには、`App.Error` interface を宣言することで想定する形にすることができます (適切なフォールバックの動作を保証するため、`message: string` を含む必要があります)。これにより、例えばユーザーがテクニカルサポートスタッフとの対応の際に引用することができるトラッキング ID を付加することができます: ```ts /// file: src/app.d.ts declare global { namespace App { interface Error { message: string; errorId: string; } } } export {}; ``` ```js /// file: src/hooks.server.js // @errors: 2322 2353 // @filename: ambient.d.ts declare module '@sentry/sveltekit' { export const init: (opts: any) => void; export const captureException: (error: any, opts: any) => void; } // @filename: index.js // ---cut--- import * as Sentry from '@sentry/sveltekit'; Sentry.init({/*...*/}) /** @type {import('@sveltejs/kit').HandleServerError} */ export async function handleError({ error, event, status, message }) { const errorId = crypto.randomUUID(); // example integration with https://sentry.io/ Sentry.captureException(error, { extra: { event, errorId, status } }); return { message: 'Whoops!', errorId }; } ``` ```js /// file: src/hooks.client.js // @errors: 2322 2353 // @filename: ambient.d.ts declare module '@sentry/sveltekit' { export const init: (opts: any) => void; export const captureException: (error: any, opts: any) => void; } // @filename: index.js // ---cut--- import * as Sentry from '@sentry/sveltekit'; Sentry.init({/*...*/}) /** @type {import('@sveltejs/kit').HandleClientError} */ export async function handleError({ error, event, status, message }) { const errorId = crypto.randomUUID(); // example integration with https://sentry.io/ Sentry.captureException(error, { extra: { event, errorId, status } }); return { message: 'Whoops!', errorId }; } ``` > [!NOTE] `src/hooks.client.js` では、`handleError` の型は `HandleServerError` ではなく `HandleClientError` で、`event` は `RequestEvent` ではなく `NavigationEvent` です。 この関数は _想定される_ エラー (`@sveltejs/kit` からインポートされる [`error`](@sveltejs-kit#error) 関数でスローされるエラー) の場合は呼び出されません。 開発中、Svelte のコードの構文エラーでエラーが発生した場合、渡される error には、エラーの場所のハイライトが付与された `frame` プロパティがあります。 > [!NOTE] `handleError` 自体が決してエラーをスローしないようにしてください。 ### init This function runs once, when the server is created or the app starts in the browser, and is a useful place to do asynchronous work such as initializing a database connection. > [!NOTE] If your environment supports top-level await, the `init` function is really no different from writing your initialisation logic at the top level of the module, but some environments — most notably, Safari — don't. ```js /// file: src/hooks.server.js import * as db from '$lib/server/database'; /** @type {import('@sveltejs/kit').ServerInit} */ export async function init() { await db.connect(); } ``` > [!NOTE] > In the browser, asynchronous work in `init` will delay hydration, so be mindful of what you put in there. ## Universal hooks 以下は `src/hooks.js` に追加することができます。universal hooks はサーバーとクライアントの両方で実行されます (shared hooks と混同しないようにしてください、shared hooks は環境依存です)。 ### reroute この関数は `handle` より前に実行され、URL をルート(route)に変換する方法を変更することができます。戻り値の pathname (デフォルトは `url.pathname`) はルート(route)パラメータを選択するのに使用されます。 例えば、`src/routes/[[lang]]/about/+page.svelte` というページがあるとして、`/en/about` や `/de/ueber-uns` や `/fr/a-propos` でアクセスできるようにしたいとします。この場合は `reroute` を使用して実装することができます: ```js /// file: src/hooks.js // @errors: 2345 // @errors: 2304 /** @type {Record} */ const translated = { '/en/about': '/en/about', '/de/ueber-uns': '/de/about', '/fr/a-propos': '/fr/about', }; /** @type {import('@sveltejs/kit').Reroute} */ export function reroute({ url }) { if (url.pathname in translated) { return translated[url.pathname]; } } ``` `lang` パラメータは戻り値の pathname から正しく導くことができます。 `reroute` を使用してもブラウザのアドレスバーの内容や `event.url` の値は変更されません。 ### transport This is a collection of _transporters_, which allow you to pass custom types — returned from `load` and form actions — across the server/client boundary. Each transporter contains an `encode` function, which encodes values on the server (or returns `false` for anything that isn't an instance of the type) and a corresponding `decode` function: ```js /// file: src/hooks.js import { Vector } from '$lib/math'; /** @type {import('@sveltejs/kit').Transport} */ export const transport = { Vector: { encode: (value) => value instanceof Vector && [value.x, value.y], decode: ([x, y]) => new Vector(x, y) } }; ``` ## その他の参考資料 - [Tutorial: Hooks](/tutorial/kit/handle) # Errors ソフトウェア開発において、エラーは避けられないものです。SvelteKit では、エラーが発生した場所、エラーの種類、受信したリクエストの性質に応じて、異なる方法でエラーを処理します。 ## Error objects SvelteKit は想定されるエラーと予期せぬエラーを区別します。どちらもデフォルトではシンプルな `{ message: string }` オブジェクトとして表現されます。 以下の例のように、`code` やトラッキング `id` を追加することができます。(TypeScript を使用する場合、[Type safety](errors#Type-safety) で説明したように `Error` 型を再定義する必要があります) ## Expected errors 想定されるエラーとは、`@sveltejs/kit` からインポートされる [`error`](@sveltejs-kit#error) を使用して作成されるものを指します: ```js /// file: src/routes/blog/[slug]/+page.server.js // @filename: ambient.d.ts declare module '$lib/server/database' { export function getPost(slug: string): Promise<{ title: string, content: string } | undefined> } // @filename: index.js // ---cut--- import { error } from '@sveltejs/kit'; import * as db from '$lib/server/database'; /** @type {import('./$types').PageServerLoad} */ export async function load({ params }) { const post = await db.getPost(params.slug); if (!post) { error(404, { message: 'Not found' }); } return { post }; } ``` これは SvelteKit が catch する例外をスローし、それによってレスポンスのステータスコードを 404 に設定し、[`+error.svelte`](routing#error) コンポーネントをレンダリングします。`page.error` は `error(...)` に第二引数として渡されたオブジェクトです。 ```svelte

{page.error.message}

``` > [!LEGACY] > `$app/state` was added in SvelteKit 2.12. If you're using an earlier version or are using Svelte 4, use `$app/stores` instead. 必要に応じて、エラーオブジェクトにプロパティを追加することができます… ```js // @filename: ambient.d.ts declare global { namespace App { interface Error { message: string; code: string; } } } export {} // @filename: index.js import { error } from '@sveltejs/kit'; // ---cut--- error(404, { message: 'Not found', +++code: 'NOT_FOUND'+++ }); ``` …追加しない場合は、便宜上、文字列を第二引数に渡すことができます: ```js import { error } from '@sveltejs/kit'; // ---cut--- ---error(404, { message: 'Not found' });--- +++error(404, 'Not found');+++ ``` > [!NOTE] [SvelteKit 1.x 系では](migrating-to-sveltekit-2#redirect-and-error-are-no-longer-thrown-by-you)、ご自身で `error` を `throw` しなければいけませんでした。 ## Unexpected errors 予期せぬエラーとは、リクエストの処理中に発生するその他の例外のことを指します。これらは機密情報を含むことがあるため、予期せぬエラーのメッセージとスタックトレースはユーザーには公開されません。 デフォルトでは、予期せぬエラーはコンソール (または、本番環境では、サーバーログ) に出力され、ユーザーに公開されるエラーはこのように汎用的な形式です。 ```json { "message": "Internal Error" } ``` 予期せぬエラーは [`handleError`](hooks#Shared-hooks-handleError) hook を通ります。ここで、独自のエラーハンドリングを追加することができます。例えば、レポーティングサービスにエラーを送ったり、カスタムのエラーオブジェクト (これは `$page.error` になります) を返したりすることができます。 ## Responses もし `handle` の中や [`+server.js`](routing#server) リクエストハンドラの中でエラーが発生した場合、SvelteKit はリクエストの `Accept` ヘッダー に応じて、フォールバックエラーページか、エラーオブジェクトの JSON 表現をレスポンスとして返します。 `src/error.html` ファイルを追加することで、フォールバックエラーページをカスタマイズすることができます: ```html %sveltekit.error.message%

My custom error page

Status: %sveltekit.status%

Message: %sveltekit.error.message%

``` SvelteKit が `%sveltekit.status%` と `%sveltekit.error.message%` を、それぞれ対応する値に置き換えます。 ページのレンダリング中に `load` 関数の中でエラーが発生した場合、SvelteKit はエラーが発生した場所に最も近い [`+error.svelte`](routing#error) コンポーネントをレンダリングします。`+layout(.server).js` の `load` 関数の内側でエラーが発生した場合、ツリーの中で最も近くにあるエラー境界はそのレイアウトの上位にある `+error.svelte` ファイルです (隣ではありません)。 例外は、最上位の `+layout.js` や `+layout.server.js` の中でエラーが発生した場合です。通常、最上位のレイアウトには `+error.svelte` コンポーネントが含まれているためです。この場合、SvelteKit はフォールバックエラーページを使用します。 ## Type safety もし TypeScript を使用していてエラーの形式をカスタマイズする必要がある場合、アプリで `App.Error` インターフェイスを宣言することでそれができます (慣習ではこれを `src/app.d.ts` に書きますが、TypeScript が '参照' することができればどこでも構いません): ```ts /// file: src/app.d.ts declare global { namespace App { interface Error { +++ code: string; id: string;+++ } } } export {}; ``` このインターフェイスは常に `message: string` プロパティを含んでいます。 ## その他の参考資料 - [Tutorial: Errors and redirects](/tutorial/kit/error-basics) - [Tutorial: Hooks](/tutorial/kit/handle) # Link options SvelteKit では、アプリのルート(routes)間の移動に、(フレームワーク固有の `` コンポーネントではなく) `
` 要素を使用します。ユーザーが、`href` がアプリのものであるリンク (外部サイトではないリンク) をクリックする場合、SvelteKit はそのコードをインポートし、データを取得するために必要な `load` 関数を呼び出して、新しいページに移動します。 `data-sveltekit-*` 属性でリンクの挙動をカスタマイズすることができます。これらは `` 自身やその親要素に適用することができます。 これらのオプションは、[`method="GET"`](form-actions#GET-vs-POST) を持つ `` 要素にも適用されます。 ## data-sveltekit-preload-data ユーザーがリンクをクリックしたことをブラウザが検知するより前に、(デスクトップでは) マウスがリンクをホバーしたことや、`touchstart` や `mousedown` がトリガーされることを検知することができます。どちらの場合も、`click` イベントが発生することを経験に基づいて推測することができます。 SvelteKit はこの情報を使ってインポートするコードやそのページのデータの取得をいち早く開始することができ、数百ミリ秒を稼ぐことができます。これが、ユーザーインターフェースが遅延していると感じるか、それともきびきび動いていると感じるかの差になります。 この動作は `data-sveltekit-preload-data` 属性によってコントロールすることができ、2つの値のうちどちらかを設定することができます: - `"hover"` は、マウスがリンクの上にきたときにプリロードを開始します。モバイルでは、`touchstart` でプリロードが開始されます - `"tap"` は、`touchstart` や `mousedown` イベントが検知されるとすぐにプリロードが開始されます デフォルトのプロジェクテンプレートには、`src/app.html` の `` 要素に `data-sveltekit-preload-data="hover"` が適用されており、デフォルトで全てのリンクがホバー時にプリロードされます: ```html
%sveltekit.body%
``` 時には、ユーザーがリンクをホバーしたときに `load` を呼ぶのは望ましくないことがあるでしょう。誤検出の可能性もありますし (必ずしもホバーに続いてクリックが発生するわけではない)、データの更新が非常に早い場合はデータが古くなってしまうこともあります。 これらの場合には、値に `"tap"` を指定します。こうすると SvelteKit は、ユーザーがリンクをタップまたはクリックしたときのみ、`load` を呼び出すようになります: ```html
Get current stonk values ``` > [!NOTE] プログラムで `$app/navigation` の `preloadData` を実行することもできます。 ユーザーがデータ使用量の削減を選択している場合、つまり [`navigator.connection.saveData`](https://developer.mozilla.org/ja/docs/Web/API/NetworkInformation/saveData) が `true` になっている場合は、データは決してプリロードされません。 ## data-sveltekit-preload-code リンク先の _データ_ をプリロードしたくない場所であっても、_コード_ をプリロードするのは有益なこともあります。`data-sveltekit-preload-code` 属性は `data-sveltekit-preload-data` と同様に動作しますが、4つの値から選択できる点が異なります。'先行度'('eagerness') の降順で並べると: - `"eager"` は、すぐにリンクをプリロードします - `"viewport"` は、リンクがビューポートに入るとすぐにプリロードします - `"hover"` - コードだけがプリロードされることを除き、上記(`data-sveltekit-preload-data` の `"hover"`)と同じです - `"tap"` - コードだけがプリロードされることを除き、上記(`data-sveltekit-preload-data` の `"tap"`)と同じです `viewport` と `eager` は、ナビゲーション直後の DOM に存在するリンクにのみ適用されることにご注意ください。リンクが後から追加された場合 (例えば `{#if ...}` ブロックの中)、`hover` や `tap` によってトリガーされるまでプリロードされません。DOM の変更を積極的に観察することによって生じてしまうパフォーマンス劣化を避けるためです。 > [!NOTE] コードのプリロードはデータのプリロードの前提条件であるため、この属性は、存在するどの `data-sveltekit-preload-data` 属性よりも先行度が高い値(more eager value)を指定した場合にのみ、効果を発揮します。 `data-sveltekit-preload-data` と同様、ユーザーがデータ使用量の削減を選択している場合、この属性も無視されます。 ## data-sveltekit-reload 時には、SvelteKit にリンクを処理させないで、ブラウザに処理をさせる必要があります。`data-sveltekit-reload` 属性をリンクに追加すると… ```html Path ``` …リンクがクリックされたときにフルページナビゲーションが発生します。 `rel="external"` 属性があるリンクも同様に扱われます。加えて、[プリレンダリング中](page-options#prerender) は無視されます。 ## data-sveltekit-replacestate ナビゲーションするときにブラウザのセッション履歴(session history)に新しいエントリを作成したくない場合があります。リンクに `data-sveltekit-replacestate` 属性を追加すると… ```html Path ``` …リンクがクリックされたときに、`pushState` で新しいエントリを作成する代わりに現在の `history` エントリを置き換えます。 ## data-sveltekit-keepfocus ナビゲーションの後に[フォーカスをリセット](accessibility#Focus-management)したくない場合があります。例えば、ユーザーが入力している途中で送信をするような検索フォームがあり、テキストの input にフォーカスを維持したい場合です。`data-sveltekit-keepfocus` 属性を追加すると… ```html
``` …ナビゲーション後も現在フォーカスされている要素にフォーカスが維持されるようになります。通常、リンクにこの属性を使用するのは避けてください、フォーカスされる要素が (その前にフォーカスされていた要素ではなく) `` タグになってしまい、スクリーンリーダーなどの支援技術を使用するユーザーはナビゲーションの後にフォーカスが移動することを期待することが多いです。また、この属性はナビゲーションの後にもまだ存在する要素にのみ使用する必要があります。もしその要素が消えてしまうと、ユーザーのフォーカスは失われてしまい、支援技術ユーザーにとって混乱した体験となってしまいます。 ## data-sveltekit-noscroll 内部のリンクに移動するとき、SvelteKit はブラウザのデフォルトのナビゲーションの挙動を模倣します: ユーザーがページの左上に来るように、スクロールポジションを `0,0` に変更します (リンクに `#hash` が含まれている場合は、ID が一致する要素までスクロールします)。 特定のケースでは、この挙動を無効化したいことがあるでしょう。`data-sveltekit-noscroll` 属性をリンクに追加すると… ```html Path ``` …リンクがクリックされたあとのスクロールを中止します。 ## Disabling options これらのオプションが有効になっている要素の中でこれらのオプションを無効にするには、`"false"` 値を使用します: ```html
a b c
d e f
``` 条件によって要素に属性を適用する場合は、このようにします: ```svelte
``` # Service workers Service Worker は、アプリ内部でネットワークリクエストを処理するプロキシサーバーとして機能します。これによりアプリをオフラインで動作させることが可能になります。もしオフラインサポートが不要な場合(または構築するアプリの種類によって現実的に実装できない場合)でも、ビルドした JS と CSS を事前にキャッシュしてナビゲーションを高速化するために Service Worker を使用する価値はあります。 SvelteKit では、`src/service-worker.js` ファイル (や `src/service-worker/index.js`) がある場合、バンドルされ、自動的に登録されます。必要に応じて、[service worker の ロケーション](configuration#files) を変更することができます。 service worker を独自のロジックで登録する必要がある場合や、その他のソリューションを使う場合は、[自動登録を無効化](configuration#serviceWorker) することができます。デフォルトの登録方法は次のようなものです: ```js if ('serviceWorker' in navigator) { addEventListener('load', function () { navigator.serviceWorker.register('./path/to/service-worker.js'); }); } ``` ## service worker の内部では service worker の内部では、[`$service-worker` モジュール]($service-worker) にアクセスでき、これによって全ての静的なアセット、ビルドファイル、プリレンダリングページへのパスが提供されます。また、アプリのバージョン文字列 (一意なキャッシュ名を作成するのに使用できます) と、デプロイメントの `base` パスが提供されます。Vite の設定に `define` (グローバル変数の置換に使用) を指定している場合、それはサーバー/クライアントのビルドだけでなく、service worker にも適用されます。 次の例では、ビルドされたアプリと `static` にあるファイルをすぐに(eagerly)キャッシュし、その他全てのリクエストはそれらの発生時にキャッシュします。これにより、各ページは一度アクセスするとオフラインで動作するようになります。 ```js // @errors: 2339 /// import { build, files, version } from '$service-worker'; // Create a unique cache name for this deployment const CACHE = `cache-${version}`; const ASSETS = [ ...build, // the app itself ...files // everything in `static` ]; self.addEventListener('install', (event) => { // Create a new cache and add all files to it async function addFilesToCache() { const cache = await caches.open(CACHE); await cache.addAll(ASSETS); } event.waitUntil(addFilesToCache()); }); self.addEventListener('activate', (event) => { // Remove previous cached data from disk async function deleteOldCaches() { for (const key of await caches.keys()) { if (key !== CACHE) await caches.delete(key); } } event.waitUntil(deleteOldCaches()); }); self.addEventListener('fetch', (event) => { // ignore POST requests etc if (event.request.method !== 'GET') return; async function respond() { const url = new URL(event.request.url); const cache = await caches.open(CACHE); // `build`/`files` can always be served from the cache if (ASSETS.includes(url.pathname)) { const response = await cache.match(url.pathname); if (response) { return response; } } // for everything else, try the network first, but // fall back to the cache if we're offline try { const response = await fetch(event.request); // if we're offline, fetch can return a value that is not a Response // instead of throwing - and we can't pass this non-Response to respondWith if (!(response instanceof Response)) { throw new Error('invalid response from fetch'); } if (response.status === 200) { cache.put(event.request, response.clone()); } return response; } catch (err) { const response = await cache.match(event.request); if (response) { return response; } // if there's no cache, then just error out // as there is nothing we can do to respond to this request throw err; } } event.respondWith(respond()); }); ``` > [!NOTE] キャッシュにはご注意ください! 場合によっては、オフラインでは利用できないデータよりも古くなったデータのほうが悪いことがあります。ブラウザはキャッシュが一杯になると空にするため、ビデオファイルのような大きなアセットをキャッシュする場合にもご注意ください。 ## 開発中は(During development) service worker はプロダクション向けにはバンドルされますが、開発中はバンドルされません。そのため、[modules in service workers](https://web.dev/es-modules-in-sw) をサポートするブラウザのみ、開発時にもそれを使用することができます。service worker を手動で登録する場合、開発時に `{ type: 'module' }` オプションを渡す必要があります: ```js import { dev } from '$app/environment'; navigator.serviceWorker.register('/service-worker.js', { type: dev ? 'module' : 'classic' }); ``` > [!NOTE] `build` と `prerendered` は開発中は空配列です ## 型安全性(Type safety) service worker に適切な型を設定するには、マニュアルで設定が必要です。`service-worker.js` の中で、ファイルの先頭に以下を追加してください: ```original-js /// /// /// /// const sw = /** @type {ServiceWorkerGlobalScope} */ (/** @type {unknown} */ (self)); ``` ```generated-ts /// /// /// /// const sw = self as unknown as ServiceWorkerGlobalScope; ``` これにより、`HTMLElement` のような service worker の中では使用できない DOM の型付けへのアクセスが無効になり、正しい global が初期化されます。`self` を `sw` に再代入することで、プロセス内で型をキャストすることができます (いくつか方法がありますが、これが追加のファイルを必要としない最も簡単な方法です)。ファイルの残りの部分では、`self` の代わりに `sw` を使用します。SvelteKit の型を参照することで、`$service-worker` import に適切な型定義があることを保証することができます。もし `$env/static/public` をインポートする場合は、そのインポートに `// @ts-ignore` を追加するか、`/// ` を reference types に追加する必要があります。 ## その他のソリューション SvelteKit の service worker 実装は意図的に低レベル(low-level)です。より本格的な、よりこだわりが強い(opinionated)ソリューションが必要な場合は、[Vite PWA plugin](https://vite-pwa-org.netlify.app/frameworks/sveltekit.html) のようなソリューションをご覧になることをおすすめしております、こちらは [Workbox](https://web.dev/learn/pwa/workbox) を使用しています。service worker に関する一般的な情報をもっとお探しであれば、[MDN web docs](https://developer.mozilla.org/ja/docs/Web/API/Service_Worker_API/Using_Service_Workers) をおすすめします。 # Server-only modules 良き友人のように、SvelteKit はあなたの秘密を守ります。バックエンドとフロントエンドが同じリポジトリにある場合、機密データをフロントエンドのコードに誤ってインポートしてしまうことが簡単に起こってしまいます (例えば、API キーを持つ環境変数など)。SvelteKit はこれを完全に防ぐ方法を提供します: サーバー専用のモジュール(server-only modules)です ## Private environment variables [`$env/static/private`]($env-static-private) モジュールと [`$env/dynamic/private`]($env-dynamic-private) モジュールは、[`hooks.server.js`](hooks#Server-hooks) や [`+page.server.js`](routing#page-page.server.js) のようなサーバー上でのみ実行されるモジュールにのみインポートすることが可能です。 ## Server-only utilities The [`$app/server`]($app-server) module, which contains a [`read`]($app-server#read) function for reading assets from the filesystem, can likewise only be imported by code that runs on the server. ## Your modules モジュールをサーバー専用にするには2通りの方法があります: - ファイル名に `.server` を付けます。例: `secrets.server.js` - モジュールを `$lib/server` に置きます。例: `$lib/server/secrets.js` ## How it works パブリックに公開されるコード (public-facing code) にサーバー専用のコードを (直接的かまたは間接的かにかかわらず) インポートすると… ```js // @errors: 7005 /// file: $lib/server/secrets.js export const atlantisCoordinates = [/* redacted */]; ``` ```js // @errors: 2307 7006 7005 /// file: src/routes/utils.js export { atlantisCoordinates } from '$lib/server/secrets.js'; export const add = (a, b) => a + b; ``` ```html /// file: src/routes/+page.svelte ``` …SvelteKit はエラーとなります: ``` Cannot import $lib/server/secrets.js into public-facing code: - src/routes/+page.svelte - src/routes/utils.js - $lib/server/secrets.js ``` パブリックに公開されるコード `src/routes/+page.svelte` は、`add` を使用しているのみで、シークレットの `atlantisCoordinates` を使用していませんが、ブラウザがダウンロードする JavaScript にシークレットなコードが残ってしまう可能性があり、このインポートチェーンは安全ではないと考えられます。 この機能は動的なインポート(dynamic imports)でも動作し、``await import(`./${foo}.js`)`` のような補完されたインポートに対しても有効ですが、小さい注意点があります。もしパブリックに公開されるコードとサーバー専用のモジュールの間に2つ以上の dynamic imports がある場合、コードが最初にロードされるときに不正なインポートが検出されない可能性があります。 > [!NOTE] Vitest のようなユニットテストフレームワークはサーバー専用のコードと公開されるコードを区別しません。そのため、テストの実行中、つまり `process.env.TEST === 'true'` となっているときは、不正なインポートの検出は無効化されます。 ## その他の参考資料 - [Tutorial: Environment variables](/tutorial/kit/env-static-private) # Snapshots 例えばサイドバーのスクロールポジションや、`` 要素の中身などの、一時的な DOM の状態(state)は、あるページから別のページに移動するときに破棄されます。 例えば、ユーザーがフォームに入力し、それを送信する前に別のページに移動してからまた戻ってきた場合や、ページをリフレッシュした場合、フォームに入力されていた値は失われます。入力内容を保持しておくことが重要な場合、DOM の状態を _スナップショット(snapshot)_ として記録することができ、ユーザーが戻ってきたときに復元することができます。 これを行うには、`+page.svelte` や `+layout.svelte` で、`capture` メソッドと `restore` メソッドを持つ `snapshot` オブジェクトをエクスポートします: ```svelte