載入資料
在渲染 +page.svelte
元件(及其包含的 +layout.svelte
元件)之前,我們通常需要取得一些資料。這是透過定義 load
函式來完成的。
頁面資料
一個 +page.svelte
檔案可以有一個兄弟檔案 +page.js
,該檔案匯出一個 load
函式,其返回值可透過 data
屬性提供給頁面
/** @type {import('./$types').PageLoad} */
export function function load({ params }: {
params: any;
}): {
post: {
title: string;
content: string;
};
}
load({ params: any
params }) {
return {
post: {
title: string;
content: string;
}
post: {
title: string
title: `Title for ${params: any
params.slug} goes here`,
content: string
content: `Content for ${params: any
params.slug} goes here`
}
};
}
import type { type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad } from './$types';
export const const load: PageLoad
load: type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad = ({ params: Record<string, any>
The parameters of the current page - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params }) => {
return {
post: {
title: string;
content: string;
}
post: {
title: string
title: `Title for ${params: Record<string, any>
The parameters of the current page - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params.slug} goes here`,
content: string
content: `Content for ${params: Record<string, any>
The parameters of the current page - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params.slug} goes here`
}
};
};
<script>
/** @type {{ data: import('./$types').PageData }} */
let { data } = $props();
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
<script lang="ts">
import type { PageData } from './$types';
let { data }: { data: PageData } = $props();
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
舊版模式
在 Svelte 4 中,您會使用
export let data
來代替
感謝產生的 $types
模組,我們獲得了完整的型別安全。
+page.js
檔案中的 load
函式會在伺服器和瀏覽器上執行(除非與 export const ssr = false
結合使用,在這種情況下,它將僅在瀏覽器中執行)。如果您的 load
函式應該始終在伺服器上執行(例如,因為它使用私有環境變數,或存取資料庫),那麼它應該放在 +page.server.js
中。
您的部落格文章的 load
函式的更實際版本,僅在伺服器上執行並從資料庫提取資料,可能如下所示
import * as module "$lib/server/database"
db from '$lib/server/database';
/** @type {import('./$types').PageServerLoad} */
export async function function load(event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>): MaybePromise<void | Record<string, any>>
load({ params: Record<string, any>
The parameters of the current route - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params }) {
return {
post: {
title: string;
content: string;
}
post: await module "$lib/server/database"
db.function getPost(slug: string): Promise<{
title: string;
content: string;
}>
getPost(params: Record<string, any>
The parameters of the current route - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params.slug)
};
}
import * as module "$lib/server/database"
db from '$lib/server/database';
import type { type PageServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageServerLoad } from './$types';
export const const load: PageServerLoad
load: type PageServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageServerLoad = async ({ params: Record<string, any>
The parameters of the current route - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params }) => {
return {
post: {
title: string;
content: string;
}
post: await module "$lib/server/database"
db.function getPost(slug: string): Promise<{
title: string;
content: string;
}>
getPost(params: Record<string, any>
The parameters of the current route - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params.slug)
};
};
請注意,類型從 PageLoad
變更為 PageServerLoad
,因為伺服器 load
函式可以存取其他引數。要了解何時使用 +page.js
以及何時使用 +page.server.js
,請參閱通用 vs 伺服器。
版面配置資料
您的 +layout.svelte
檔案也可以透過 +layout.js
或 +layout.server.js
載入資料。
import * as module "$lib/server/database"
db from '$lib/server/database';
/** @type {import('./$types').LayoutServerLoad} */
export async function function load(event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>): MaybePromise<void | Record<string, any>>
load() {
return {
posts: {
title: string;
slug: string;
}[]
posts: await module "$lib/server/database"
db.function getPostSummaries(): Promise<Array<{
title: string;
slug: string;
}>>
getPostSummaries()
};
}
import * as module "$lib/server/database"
db from '$lib/server/database';
import type { type LayoutServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
LayoutServerLoad } from './$types';
export const const load: LayoutServerLoad
load: type LayoutServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
LayoutServerLoad = async () => {
return {
posts: {
title: string;
slug: string;
}[]
posts: await module "$lib/server/database"
db.function getPostSummaries(): Promise<Array<{
title: string;
slug: string;
}>>
getPostSummaries()
};
};
<script>
/** @type {{ data: import('./$types').LayoutData, children: Snippet }} */
let { data, children } = $props();
</script>
<main>
<!-- +page.svelte is `@render`ed here -->
{@render children()}
</main>
<aside>
<h2>More posts</h2>
<ul>
{#each data.posts as post}
<li>
<a href="/blog/{post.slug}">
{post.title}
</a>
</li>
{/each}
</ul>
</aside>
<script lang="ts">
import type { LayoutData } from './$types';
let { data, children }: { data: LayoutData, children: Snippet } = $props();
</script>
<main>
<!-- +page.svelte is `@render`ed here -->
{@render children()}
</main>
<aside>
<h2>More posts</h2>
<ul>
{#each data.posts as post}
<li>
<a href="/blog/{post.slug}">
{post.title}
</a>
</li>
{/each}
</ul>
</aside>
從版面配置 load
函式傳回的資料可提供給子 +layout.svelte
元件和 +page.svelte
元件,以及它「屬於」的版面配置。
<script>
import { page } from '$app/stores';
/** @type {{ data: import('./$types').PageData }} */
let { data } = $props();
// we can access `data.posts` because it's returned from
// the parent layout `load` function
let index = $derived(data.posts.findIndex(post => post.slug === $page.params.slug));
let next = $derived(data.posts[index + 1]);
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
{#if next}
<p>Next post: <a href="/blog/{next.slug}">{next.title}</a></p>
{/if}
<script lang="ts">
import { page } from '$app/stores';
import type { PageData } from './$types';
let { data }: { data: PageData } = $props();
// we can access `data.posts` because it's returned from
// the parent layout `load` function
let index = $derived(data.posts.findIndex(post => post.slug === $page.params.slug));
let next = $derived(data.posts[index + 1]);
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
{#if next}
<p>Next post: <a href="/blog/{next.slug}">{next.title}</a></p>
{/if}
如果多個
load
函式傳回具有相同鍵的資料,則最後一個「勝出」 — 版面配置load
傳回{ a: 1, b: 2 }
和頁面load
傳回{ b: 3, c: 4 }
的結果將是{ a: 1, b: 3, c: 4 }
。
$page.data
+page.svelte
元件,以及其上方的每個 +layout.svelte
元件,都可以存取其自身的資料以及來自其所有父系的資料。
在某些情況下,我們可能需要相反的情況 — 父版面配置可能需要存取頁面資料或來自子版面配置的資料。例如,根版面配置可能想要存取從 +page.js
或 +page.server.js
中的 load
函式傳回的 title
屬性。這可以使用 $page.data
來完成
<script>
import { page } from '$app/stores';
</script>
<svelte:head>
<title>{$page.data.title}</title>
</svelte:head>
$page.data
的類型資訊由 App.PageData
提供。
通用 vs 伺服器
正如我們所看到的,有兩種型別的 load
函式
+page.js
和+layout.js
檔案匯出通用load
函式,這些函式會在伺服器和瀏覽器上執行+page.server.js
和+layout.server.js
檔案匯出伺服器load
函式,這些函式僅在伺服器端執行
從概念上講,它們是相同的,但是有一些重要的差異需要注意。
哪個載入函式何時執行?
伺服器 load
函式始終在伺服器上執行。
預設情況下,通用 load
函式會在使用者首次造訪您的頁面時,於 SSR 期間在伺服器上執行。然後,它們會在 hydration 期間再次執行,重複使用來自 fetch 請求的任何回應。所有後續的通用 load
函式呼叫都發生在瀏覽器中。您可以透過頁面選項自訂行為。如果您停用伺服器端渲染,您將獲得一個 SPA,並且通用 load
函式始終在用戶端上執行。
如果路由同時包含通用和伺服器 load
函式,則伺服器 load
會先執行。
除非您預先渲染頁面,否則會在執行時期呼叫 load
函式 — 在這種情況下,它會在建置時呼叫。
輸入
通用和伺服器 load
函式都可以存取描述請求的屬性 (params
、route
和 url
) 以及各種函式 (fetch
、setHeaders
、parent
、depends
和 untrack
)。這些將在以下章節中說明。
使用 ServerLoadEvent
呼叫伺服器 load
函式,該事件會繼承來自 RequestEvent
的 clientAddress
、cookies
、locals
、platform
和 request
。
使用 LoadEvent
呼叫通用 load
函式,該事件具有 data
屬性。如果您的 +page.js
和 +page.server.js
(或 +layout.js
和 +layout.server.js
)中都有 load
函式,則伺服器 load
函式的傳回值是通用 load
函式引數的 data
屬性。
輸出
通用 load
函式可以傳回包含任何值的物件,包括自訂類別和元件建構子。
伺服器 load
函式必須傳回可以使用 devalue 序列化的資料 — 任何可以表示為 JSON 的內容,加上諸如 BigInt
、Date
、Map
、Set
和 RegExp
之類的內容,或重複/循環的參考 — 以便可以在網路上傳輸。您的資料可以包含 promises,在這種情況下,它將串流到瀏覽器。
何時使用哪個
當您需要直接從資料庫或檔案系統存取資料,或需要使用私有環境變數時,伺服器 load
函式會很方便。
當您需要從外部 API fetch
資料並且不需要私有憑證時,通用 load
函式會很有用,因為 SvelteKit 可以直接從 API 取得資料,而不是透過您的伺服器。當您需要傳回無法序列化的內容(例如 Svelte 元件建構子)時,它們也很有用。
在極少數情況下,您可能需要同時使用兩者 — 例如,您可能需要傳回使用來自伺服器的資料初始化的自訂類別的實例。當同時使用兩者時,伺服器 load
傳回值不會直接傳遞到頁面,而是傳遞到通用 load
函式(作為 data
屬性)
/** @type {import('./$types').PageServerLoad} */
export async function function load(): Promise<{
serverMessage: string;
}>
load() {
return {
serverMessage: string
serverMessage: 'hello from server load function'
};
}
import type { type PageServerLoad = (event: Kit.ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageServerLoad = (event: Kit.ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageServerLoad } from './$types';
export const const load: PageServerLoad
load: type PageServerLoad = (event: Kit.ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageServerLoad = (event: Kit.ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageServerLoad = async () => {
return {
serverMessage: string
serverMessage: 'hello from server load function'
};
};
/** @type {import('./$types').PageLoad} */
export async function function load({ data }: {
data: any;
}): Promise<{
serverMessage: any;
universalMessage: string;
}>
load({ data: any
data }) {
return {
serverMessage: any
serverMessage: data: any
data.serverMessage,
universalMessage: string
universalMessage: 'hello from universal load function'
};
}
import type { type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad } from './$types';
export const const load: PageLoad
load: type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad = async ({ data: Record<string, any> | null
Contains the data returned by the route’s server load
function (in +layout.server.js
or +page.server.js
), if any.
data }) => {
return {
serverMessage: any
serverMessage: data: Record<string, any> | null
Contains the data returned by the route’s server load
function (in +layout.server.js
or +page.server.js
), if any.
data.serverMessage,
universalMessage: string
universalMessage: 'hello from universal load function'
};
};
使用 URL 資料
通常,load
函式會以某種方式依賴 URL。為此,load
函式會為您提供 url
、route
和 params
。
url
一個 URL
的實例,包含諸如 origin
、hostname
、pathname
和 searchParams
之類的屬性(其中包含解析的查詢字串,作為 URLSearchParams
物件)。url.hash
無法在 load
期間存取,因為它在伺服器上不可用。
在某些環境中,這是從伺服器端渲染期間的請求標頭衍生而來的。例如,如果您使用adapter-node,您可能需要設定轉接器才能使 URL 正確。
route
包含相對於 src/routes
的目前路由目錄的名稱
/** @type {import('./$types').PageLoad} */
export function function load({ route }: {
route: any;
}): void
load({ route: any
route }) {
var console: Console
The console
module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
- A
Console
class with methods such as console.log()
, console.error()
and console.warn()
that can be used to write to any Node.js stream.
- A global
console
instance configured to write to process.stdout
and
process.stderr
. The global console
can be used without calling require('console')
.
Warning: The global console object’s methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O
for
more information.
Example using the global console
:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console
class:
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to stdout
with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()
).
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
See util.format()
for more information.
log(route: any
route.id); // '/a/[b]/[...c]'
}
import type { type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad } from './$types';
export const const load: PageLoad
load: type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad = ({ route: {
id: string | null;
}
Info about the current route
route }) => {
var console: Console
The console
module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
- A
Console
class with methods such as console.log()
, console.error()
and console.warn()
that can be used to write to any Node.js stream.
- A global
console
instance configured to write to process.stdout
and
process.stderr
. The global console
can be used without calling require('console')
.
Warning: The global console object’s methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O
for
more information.
Example using the global console
:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console
class:
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to stdout
with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()
).
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
See util.format()
for more information.
log(route: {
id: string | null;
}
Info about the current route
route.id: string | null
The ID of the current route - e.g. for src/routes/blog/[slug]
, it would be /blog/[slug]
id); // '/a/[b]/[...c]'
};
params
params
是從 url.pathname
和 route.id
衍生而來的。
給定 /a/[b]/[...c]
的 route.id
和 /a/x/y/z
的 url.pathname
,params
物件看起來會像這樣
{
"b": "x",
"c": "y/z"
}
發出 fetch 請求
若要從外部 API 或 +server.js
處理程式取得資料,您可以使用提供的 fetch
函式,其行為與 原生 fetch
Web API 完全相同,但有一些其他功能
- 它可以用於在伺服器上發出帶有憑證的請求,因為它會繼承頁面請求的
cookie
和authorization
標頭。 - 它可以在伺服器上發出相對請求(通常,
fetch
在伺服器內容中使用時需要具有來源的 URL)。 - 內部請求(例如,針對
+server.js
路由)在伺服器上執行時會直接前往處理函式,而不會產生 HTTP 呼叫的開銷。 - 在伺服器端渲染期間,回應將會被擷取並透過掛勾
Response
物件的text
、json
和arrayBuffer
方法,內嵌到渲染的 HTML 中。請注意,標頭將不會被序列化,除非透過filterSerializedResponseHeaders
明確包含。 - 在 hydration 期間,回應將從 HTML 讀取,保證一致性並防止額外的網路請求 - 如果您在使用瀏覽器
fetch
而不是load
fetch
時在瀏覽器主控台中收到警告,這就是原因。
/** @type {import('./$types').PageLoad} */
export async function function load({ fetch, params }: {
fetch: any;
params: any;
}): Promise<{
item: any;
}>
load({ fetch: any
fetch, params: any
params }) {
const const res: any
res = await fetch: any
fetch(`/api/items/${params: any
params.id}`);
const const item: any
item = await const res: any
res.json();
return { item: any
item };
}
import type { type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad } from './$types';
export const const load: PageLoad
load: type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad = async ({ fetch: {
(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>;
}
fetch
is equivalent to the native fetch
web API, with a few additional features:
- It can be used to make credentialed requests on the server, as it inherits the
cookie
and authorization
headers for the page request.
- It can make relative requests on the server (ordinarily,
fetch
requires a URL with an origin when used in a server context).
- Internal requests (e.g. for
+server.js
routes) go directly to the handler function when running on the server, without the overhead of an HTTP call.
- During server-side rendering, the response will be captured and inlined into the rendered HTML by hooking into the
text
and json
methods of the Response
object. Note that headers will not be serialized, unless explicitly included via filterSerializedResponseHeaders
- During hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request.
You can learn more about making credentialed requests with cookies here
fetch, params: Record<string, any>
The parameters of the current page - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params }) => {
const const res: Response
res = await fetch: (input: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response> (+1 overload)
fetch(`/api/items/${params: Record<string, any>
The parameters of the current page - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params.id}`);
const const item: any
item = await const res: Response
res.Body.json(): Promise<any>
json();
return { item: any
item };
};
Cookie
伺服器端的 load
函式可以取得和設定 cookies
。
import * as module "$lib/server/database"
db from '$lib/server/database';
/** @type {import('./$types').LayoutServerLoad} */
export async function function load(event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>): MaybePromise<void | Record<string, any>>
load({ cookies: Cookies
Get or set cookies related to the current request
cookies }) {
const const sessionid: string | undefined
sessionid = cookies: Cookies
Get or set cookies related to the current request
cookies.Cookies.get(name: string, opts?: CookieParseOptions): string | undefined
Gets a cookie that was previously set with cookies.set
, or from the request headers.
get('sessionid');
return {
user: {
name: string;
avatar: string;
}
user: await module "$lib/server/database"
db.function getUser(sessionid: string | undefined): Promise<{
name: string;
avatar: string;
}>
getUser(const sessionid: string | undefined
sessionid)
};
}
import * as module "$lib/server/database"
db from '$lib/server/database';
import type { type LayoutServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
LayoutServerLoad } from './$types';
export const const load: LayoutServerLoad
load: type LayoutServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
LayoutServerLoad = async ({ cookies: Cookies
Get or set cookies related to the current request
cookies }) => {
const const sessionid: string | undefined
sessionid = cookies: Cookies
Get or set cookies related to the current request
cookies.Cookies.get(name: string, opts?: CookieParseOptions): string | undefined
Gets a cookie that was previously set with cookies.set
, or from the request headers.
get('sessionid');
return {
user: {
name: string;
avatar: string;
}
user: await module "$lib/server/database"
db.function getUser(sessionid: string | undefined): Promise<{
name: string;
avatar: string;
}>
getUser(const sessionid: string | undefined
sessionid)
};
};
只有在目標主機與 SvelteKit 應用程式相同,或是它的更特定子網域時,才會透過提供的 fetch
函式傳遞 Cookie。
例如,如果 SvelteKit 服務於 my.domain.com
- domain.com 將不會收到 Cookie
- my.domain.com 將會收到 Cookie
- api.domain.com 將不會收到 Cookie
- sub.my.domain.com 將會收到 Cookie
當設定 credentials: 'include'
時,其他 Cookie 將不會被傳遞,因為 SvelteKit 不知道哪個 Cookie 屬於哪個網域(瀏覽器不會傳遞此資訊),因此轉發任何 Cookie 都是不安全的。請使用 handleFetch hook 來解決此問題。
標頭
伺服器端和通用 load
函式都可以存取 setHeaders
函式,該函式在伺服器端執行時,可以設定回應的標頭。(在瀏覽器中執行時,setHeaders
無效。)這在您希望頁面被快取時很有用,例如:
/** @type {import('./$types').PageLoad} */
export async function function load({ fetch, setHeaders }: {
fetch: any;
setHeaders: any;
}): Promise<any>
load({ fetch: any
fetch, setHeaders: any
setHeaders }) {
const const url: "https://cms.example.com/products.json"
url = `https://cms.example.com/products.json`;
const const response: any
response = await fetch: any
fetch(const url: "https://cms.example.com/products.json"
url);
// Headers are only set during SSR, caching the page's HTML
// for the same length of time as the underlying data.
setHeaders: any
setHeaders({
age: any
age: const response: any
response.headers.get('age'),
'cache-control': const response: any
response.headers.get('cache-control')
});
return const response: any
response.json();
}
import type { type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad } from './$types';
export const const load: PageLoad
load: type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad = async ({ fetch: {
(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>;
}
fetch
is equivalent to the native fetch
web API, with a few additional features:
- It can be used to make credentialed requests on the server, as it inherits the
cookie
and authorization
headers for the page request.
- It can make relative requests on the server (ordinarily,
fetch
requires a URL with an origin when used in a server context).
- Internal requests (e.g. for
+server.js
routes) go directly to the handler function when running on the server, without the overhead of an HTTP call.
- During server-side rendering, the response will be captured and inlined into the rendered HTML by hooking into the
text
and json
methods of the Response
object. Note that headers will not be serialized, unless explicitly included via filterSerializedResponseHeaders
- During hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request.
You can learn more about making credentialed requests with cookies here
fetch, setHeaders: (headers: Record<string, string>) => void
If you need to set headers for the response, you can do so using the this method. This is useful if you want the page to be cached, for example:
src/routes/blog/+pageexport async function load({ fetch, setHeaders }) {
const url = `https://cms.example.com/articles.json`;
const response = await fetch(url);
setHeaders({
age: response.headers.get('age'),
'cache-control': response.headers.get('cache-control')
});
return response.json();
}
Setting the same header multiple times (even in separate load
functions) is an error — you can only set a given header once.
You cannot add a set-cookie
header with setHeaders
— use the cookies
API in a server-only load
function instead.
setHeaders
has no effect when a load
function runs in the browser.
setHeaders }) => {
const const url: "https://cms.example.com/products.json"
url = `https://cms.example.com/products.json`;
const const response: Response
response = await fetch: (input: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response> (+1 overload)
fetch(const url: "https://cms.example.com/products.json"
url);
// Headers are only set during SSR, caching the page's HTML
// for the same length of time as the underlying data.
setHeaders: (headers: Record<string, string>) => void
If you need to set headers for the response, you can do so using the this method. This is useful if you want the page to be cached, for example:
src/routes/blog/+pageexport async function load({ fetch, setHeaders }) {
const url = `https://cms.example.com/articles.json`;
const response = await fetch(url);
setHeaders({
age: response.headers.get('age'),
'cache-control': response.headers.get('cache-control')
});
return response.json();
}
Setting the same header multiple times (even in separate load
functions) is an error — you can only set a given header once.
You cannot add a set-cookie
header with setHeaders
— use the cookies
API in a server-only load
function instead.
setHeaders
has no effect when a load
function runs in the browser.
setHeaders({
age: string | null
age: const response: Response
response.Response.headers: Headers
headers.Headers.get(name: string): string | null
get('age'),
'cache-control': const response: Response
response.Response.headers: Headers
headers.Headers.get(name: string): string | null
get('cache-control')
});
return const response: Response
response.Body.json(): Promise<any>
json();
};
多次設定相同的標頭(即使在不同的 load
函式中)會產生錯誤。您只能使用 setHeaders
函式設定給定的標頭一次。您不能使用 setHeaders
新增 set-cookie
標頭,請改用 cookies.set(name, value, options)
。
使用父層資料
有時,load
函式存取父層 load
函式的資料會很有用,可以使用 await parent()
來實現。
/** @type {import('./$types').LayoutLoad} */
export function function load(): {
a: number;
}
load() {
return { a: number
a: 1 };
}
import type { type LayoutLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type LayoutLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
LayoutLoad } from './$types';
export const const load: LayoutLoad
load: type LayoutLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type LayoutLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
LayoutLoad = () => {
return { a: number
a: 1 };
};
/** @type {import('./$types').LayoutLoad} */
export async function function load({ parent }: {
parent: any;
}): Promise<{
b: any;
}>
load({ parent: any
parent }) {
const { const a: any
a } = await parent: any
parent();
return { b: any
b: const a: any
a + 1 };
}
import type { type LayoutLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type LayoutLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
LayoutLoad } from './$types';
export const const load: LayoutLoad
load: type LayoutLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type LayoutLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
LayoutLoad = async ({ parent: () => Promise<Record<string, any>>
await parent()
returns data from parent +layout.js
load
functions.
Implicitly, a missing +layout.js
is treated as a ({ data }) => data
function, meaning that it will return and forward data from parent +layout.server.js
files.
Be careful not to introduce accidental waterfalls when using await parent()
. If for example you only want to merge parent data into the returned output, call it after fetching your other data.
parent }) => {
const { const a: any
a } = await parent: () => Promise<Record<string, any>>
await parent()
returns data from parent +layout.js
load
functions.
Implicitly, a missing +layout.js
is treated as a ({ data }) => data
function, meaning that it will return and forward data from parent +layout.server.js
files.
Be careful not to introduce accidental waterfalls when using await parent()
. If for example you only want to merge parent data into the returned output, call it after fetching your other data.
parent();
return { b: any
b: const a: any
a + 1 };
};
/** @type {import('./$types').PageLoad} */
export async function function load({ parent }: {
parent: any;
}): Promise<{
c: any;
}>
load({ parent: any
parent }) {
const { const a: any
a, const b: any
b } = await parent: any
parent();
return { c: any
c: const a: any
a + const b: any
b };
}
import type { type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad } from './$types';
export const const load: PageLoad
load: type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad = async ({ parent: () => Promise<Record<string, any>>
await parent()
returns data from parent +layout.js
load
functions.
Implicitly, a missing +layout.js
is treated as a ({ data }) => data
function, meaning that it will return and forward data from parent +layout.server.js
files.
Be careful not to introduce accidental waterfalls when using await parent()
. If for example you only want to merge parent data into the returned output, call it after fetching your other data.
parent }) => {
const { const a: any
a, const b: any
b } = await parent: () => Promise<Record<string, any>>
await parent()
returns data from parent +layout.js
load
functions.
Implicitly, a missing +layout.js
is treated as a ({ data }) => data
function, meaning that it will return and forward data from parent +layout.server.js
files.
Be careful not to introduce accidental waterfalls when using await parent()
. If for example you only want to merge parent data into the returned output, call it after fetching your other data.
parent();
return { c: any
c: const a: any
a + const b: any
b };
};
<script>
/** @type {{ data: import('./$types').PageData }} */
let { data } = $props();
</script>
<!-- renders `1 + 2 = 3` -->
<p>{data.a} + {data.b} = {data.c}</p>
<script lang="ts">
import type { PageData } from './$types';
let { data }: { data: PageData } = $props();
</script>
<!-- renders `1 + 2 = 3` -->
<p>{data.a} + {data.b} = {data.c}</p>
請注意,
+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.server.js
檔案的資料,而這些檔案沒有被 +layout.js
檔案「遮蔽」。
使用 await parent()
時,請注意不要引入瀑布效應。例如,這裡的 getData(params)
不依賴於呼叫 parent()
的結果,因此我們應該先呼叫它,以避免延遲渲染。
/** @type {import('./$types').PageLoad} */
export async function function load(event: LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>): MaybePromise<void | Record<string, any>>
load({ params: Record<string, any>
The parameters of the current page - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params, parent: () => Promise<Record<string, any>>
await parent()
returns data from parent +layout.js
load
functions.
Implicitly, a missing +layout.js
is treated as a ({ data }) => data
function, meaning that it will return and forward data from parent +layout.server.js
files.
Be careful not to introduce accidental waterfalls when using await parent()
. If for example you only want to merge parent data into the returned output, call it after fetching your other data.
parent }) {
const parentData = await parent();
const const data: {
meta: any;
}
data = await function getData(params: Record<string, string>): Promise<{
meta: any;
}>
getData(params: Record<string, any>
The parameters of the current page - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params);
const const parentData: Record<string, any>
parentData = await parent: () => Promise<Record<string, any>>
await parent()
returns data from parent +layout.js
load
functions.
Implicitly, a missing +layout.js
is treated as a ({ data }) => data
function, meaning that it will return and forward data from parent +layout.server.js
files.
Be careful not to introduce accidental waterfalls when using await parent()
. If for example you only want to merge parent data into the returned output, call it after fetching your other data.
parent();
return {
...const data: {
meta: any;
}
data,
meta: any
meta: { ...const parentData: Record<string, any>
parentData.meta, ...const data: {
meta: any;
}
data.meta: any
meta }
};
}
import type { type PageLoad = (event: LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad } from './$types';
export const const load: PageLoad
load: type PageLoad = (event: LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad = async ({ params: Record<string, any>
The parameters of the current page - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params, parent: () => Promise<Record<string, any>>
await parent()
returns data from parent +layout.js
load
functions.
Implicitly, a missing +layout.js
is treated as a ({ data }) => data
function, meaning that it will return and forward data from parent +layout.server.js
files.
Be careful not to introduce accidental waterfalls when using await parent()
. If for example you only want to merge parent data into the returned output, call it after fetching your other data.
parent }) => {
const parentData = await parent();
const const data: {
meta: any;
}
data = await function getData(params: Record<string, string>): Promise<{
meta: any;
}>
getData(params: Record<string, any>
The parameters of the current page - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params);
const const parentData: Record<string, any>
parentData = await parent: () => Promise<Record<string, any>>
await parent()
returns data from parent +layout.js
load
functions.
Implicitly, a missing +layout.js
is treated as a ({ data }) => data
function, meaning that it will return and forward data from parent +layout.server.js
files.
Be careful not to introduce accidental waterfalls when using await parent()
. If for example you only want to merge parent data into the returned output, call it after fetching your other data.
parent();
return {
...const data: {
meta: any;
}
data,
meta: any
meta: { ...const parentData: Record<string, any>
parentData.meta, ...const data: {
meta: any;
}
data.meta: any
meta }
};
};
錯誤
如果在 load
期間拋出錯誤,將會渲染最接近的 +error.svelte
。對於預期的錯誤,請使用來自 @sveltejs/kit
的 error
輔助函式來指定 HTTP 狀態碼和可選訊息
import { function error(status: number, body: App.Error): never (+1 overload)
Throws an error with a HTTP status code and an optional message.
When called during request handling, this will cause SvelteKit to
return an error response without invoking handleError
.
Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.
error } from '@sveltejs/kit';
/** @type {import('./$types').LayoutServerLoad} */
export function function load(event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>): MaybePromise<void | Record<string, any>>
load({ locals: App.Locals
Contains custom data that was added to the request within the server handle hook
.
locals }) {
if (!locals: App.Locals
Contains custom data that was added to the request within the server handle hook
.
locals.App.Locals.user?: {
name: string;
isAdmin: boolean;
} | undefined
user) {
function error(status: number, body?: {
message: string;
} extends App.Error ? App.Error | string | undefined : never): never (+1 overload)
Throws an error with a HTTP status code and an optional message.
When called during request handling, this will cause SvelteKit to
return an error response without invoking handleError
.
Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.
error(401, 'not logged in');
}
if (!locals: App.Locals
Contains custom data that was added to the request within the server handle hook
.
locals.App.Locals.user?: {
name: string;
isAdmin: boolean;
}
user.isAdmin: boolean
isAdmin) {
function error(status: number, body?: {
message: string;
} extends App.Error ? App.Error | string | undefined : never): never (+1 overload)
Throws an error with a HTTP status code and an optional message.
When called during request handling, this will cause SvelteKit to
return an error response without invoking handleError
.
Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.
error(403, 'not an admin');
}
}
import { function error(status: number, body: App.Error): never (+1 overload)
Throws an error with a HTTP status code and an optional message.
When called during request handling, this will cause SvelteKit to
return an error response without invoking handleError
.
Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.
error } from '@sveltejs/kit';
import type { type LayoutServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
LayoutServerLoad } from './$types';
export const const load: LayoutServerLoad
load: type LayoutServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
LayoutServerLoad = ({ locals: App.Locals
Contains custom data that was added to the request within the server handle hook
.
locals }) => {
if (!locals: App.Locals
Contains custom data that was added to the request within the server handle hook
.
locals.App.Locals.user?: {
name: string;
isAdmin: boolean;
} | undefined
user) {
function error(status: number, body?: {
message: string;
} extends App.Error ? App.Error | string | undefined : never): never (+1 overload)
Throws an error with a HTTP status code and an optional message.
When called during request handling, this will cause SvelteKit to
return an error response without invoking handleError
.
Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.
error(401, 'not logged in');
}
if (!locals: App.Locals
Contains custom data that was added to the request within the server handle hook
.
locals.App.Locals.user?: {
name: string;
isAdmin: boolean;
}
user.isAdmin: boolean
isAdmin) {
function error(status: number, body?: {
message: string;
} extends App.Error ? App.Error | string | undefined : never): never (+1 overload)
Throws an error with a HTTP status code and an optional message.
When called during request handling, this will cause SvelteKit to
return an error response without invoking handleError
.
Make sure you’re not catching the thrown error, which would prevent SvelteKit from handling it.
error(403, 'not an admin');
}
};
呼叫 error(...)
將會拋出例外,使其可以輕鬆地從輔助函式內部停止執行。
如果拋出非預期的錯誤,SvelteKit 將會調用 handleError
並將其視為 500 內部錯誤。
在 SvelteKit 1.x 中,您必須自己
throw
錯誤
重新導向
若要重新導向使用者,請使用來自 @sveltejs/kit
的 redirect
輔助函式來指定應重新導向的位置以及 3xx
狀態碼。如同 error(...)
,呼叫 redirect(...)
也會拋出例外,使其可以輕鬆地從輔助函式內部停止執行。
import { function redirect(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | ({} & number), location: string | URL): never
Redirect a request. When called during request handling, SvelteKit will return a redirect response.
Make sure you’re not catching the thrown redirect, which would prevent SvelteKit from handling it.
redirect } from '@sveltejs/kit';
/** @type {import('./$types').LayoutServerLoad} */
export function function load(event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>): MaybePromise<void | Record<string, any>>
load({ locals: App.Locals
Contains custom data that was added to the request within the server handle hook
.
locals }) {
if (!locals: App.Locals
Contains custom data that was added to the request within the server handle hook
.
locals.App.Locals.user?: {
name: string;
} | undefined
user) {
function redirect(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | ({} & number), location: string | URL): never
Redirect a request. When called during request handling, SvelteKit will return a redirect response.
Make sure you’re not catching the thrown redirect, which would prevent SvelteKit from handling it.
redirect(307, '/login');
}
}
import { function redirect(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | ({} & number), location: string | URL): never
Redirect a request. When called during request handling, SvelteKit will return a redirect response.
Make sure you’re not catching the thrown redirect, which would prevent SvelteKit from handling it.
redirect } from '@sveltejs/kit';
import type { type LayoutServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
LayoutServerLoad } from './$types';
export const const load: LayoutServerLoad
load: type LayoutServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
LayoutServerLoad = ({ locals: App.Locals
Contains custom data that was added to the request within the server handle hook
.
locals }) => {
if (!locals: App.Locals
Contains custom data that was added to the request within the server handle hook
.
locals.App.Locals.user?: {
name: string;
} | undefined
user) {
function redirect(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | ({} & number), location: string | URL): never
Redirect a request. When called during request handling, SvelteKit will return a redirect response.
Make sure you’re not catching the thrown redirect, which would prevent SvelteKit from handling it.
redirect(307, '/login');
}
};
請勿在
try {...}
區塊內使用redirect()
,因為重新導向會立即觸發 catch 語句。
在瀏覽器中,您也可以使用 goto
從 $app.navigation
在 load
函式之外以程式方式導航。
在 SvelteKit 1.x 中,您必須自己
throw
redirect
使用 Promise 進行串流
當使用伺服器端 load
時,Promise 會在解析時串流至瀏覽器。如果您有緩慢、非必要性的資料,這會很有用,因為您可以在所有資料都可用之前開始渲染頁面
/** @type {import('./$types').PageServerLoad} */
export async function function load(event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>): MaybePromise<void | Record<string, any>>
load({ params: Record<string, any>
The parameters of the current route - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params }) {
return {
// make sure the `await` happens at the end, otherwise we
// can't start loading comments until we've loaded the post
comments: Promise<{
content: string;
}>
comments: const loadComments: (slug: string) => Promise<{
content: string;
}>
loadComments(params: Record<string, any>
The parameters of the current route - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params.slug),
post: {
title: string;
content: string;
}
post: await const loadPost: (slug: string) => Promise<{
title: string;
content: string;
}>
loadPost(params: Record<string, any>
The parameters of the current route - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params.slug)
};
}
import type { type PageServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageServerLoad } from './$types';
export const const load: PageServerLoad
load: type PageServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageServerLoad = async ({ params: Record<string, any>
The parameters of the current route - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params }) => {
return {
// make sure the `await` happens at the end, otherwise we
// can't start loading comments until we've loaded the post
comments: Promise<{
content: string;
}>
comments: const loadComments: (slug: string) => Promise<{
content: string;
}>
loadComments(params: Record<string, any>
The parameters of the current route - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params.slug),
post: {
title: string;
content: string;
}
post: await const loadPost: (slug: string) => Promise<{
title: string;
content: string;
}>
loadPost(params: Record<string, any>
The parameters of the current route - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params.slug)
};
};
這對於建立骨架載入狀態很有用,例如:
<script>
/** @type {{ data: import('./$types').PageData }} */
let { data } = $props();
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
{#await data.comments}
Loading comments...
{:then comments}
{#each comments as comment}
<p>{comment.content}</p>
{/each}
{:catch error}
<p>error loading comments: {error.message}</p>
{/await}
<script lang="ts">
import type { PageData } from './$types';
let { data }: { data: PageData } = $props();
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
{#await data.comments}
Loading comments...
{:then comments}
{#each comments as comment}
<p>{comment.content}</p>
{/each}
{:catch error}
<p>error loading comments: {error.message}</p>
{/await}
串流資料時,請小心正確處理 Promise 拒絕。更具體地說,如果延遲載入的 Promise 在開始渲染之前(此時會被捕獲)失敗,而且沒有以某種方式處理錯誤,則伺服器可能會崩潰並出現「未處理的 Promise 拒絕」錯誤。當在 load
函式中直接使用 SvelteKit 的 fetch
時,SvelteKit 會為您處理這種情況。對於其他 Promise,只要將一個 noop-catch
連接到 Promise,將其標記為已處理即可。
/** @type {import('./$types').PageServerLoad} */
export function function load({ fetch }: {
fetch: any;
}): {
ok_manual: Promise<never>;
ok_fetch: any;
dangerous_unhandled: Promise<never>;
}
load({ fetch: any
fetch }) {
const const ok_manual: Promise<never>
ok_manual = var Promise: PromiseConstructor
Represents the completion of an asynchronous operation
Promise.PromiseConstructor.reject<never>(reason?: any): Promise<never>
Creates a new rejected promise for the provided reason.
reject();
const ok_manual: Promise<never>
ok_manual.Promise<never>.catch<void>(onrejected?: ((reason: any) => void | PromiseLike<void>) | null | undefined): Promise<void>
Attaches a callback for only the rejection of the Promise.
catch(() => {});
return {
ok_manual: Promise<never>
ok_manual,
ok_fetch: any
ok_fetch: fetch: any
fetch('/fetch/that/could/fail'),
dangerous_unhandled: Promise<never>
dangerous_unhandled: var Promise: PromiseConstructor
Represents the completion of an asynchronous operation
Promise.PromiseConstructor.reject<never>(reason?: any): Promise<never>
Creates a new rejected promise for the provided reason.
reject()
};
}
import type { type PageServerLoad = (event: Kit.ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageServerLoad = (event: Kit.ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageServerLoad } from './$types';
export const const load: PageServerLoad
load: type PageServerLoad = (event: Kit.ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageServerLoad = (event: Kit.ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageServerLoad = ({ fetch: {
(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>;
}
fetch
is equivalent to the native fetch
web API, with a few additional features:
- It can be used to make credentialed requests on the server, as it inherits the
cookie
and authorization
headers for the page request.
- It can make relative requests on the server (ordinarily,
fetch
requires a URL with an origin when used in a server context).
- Internal requests (e.g. for
+server.js
routes) go directly to the handler function when running on the server, without the overhead of an HTTP call.
- During server-side rendering, the response will be captured and inlined into the rendered HTML by hooking into the
text
and json
methods of the Response
object. Note that headers will not be serialized, unless explicitly included via filterSerializedResponseHeaders
- During hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request.
You can learn more about making credentialed requests with cookies here
fetch }) => {
const const ok_manual: Promise<never>
ok_manual = var Promise: PromiseConstructor
Represents the completion of an asynchronous operation
Promise.PromiseConstructor.reject<never>(reason?: any): Promise<never>
Creates a new rejected promise for the provided reason.
reject();
const ok_manual: Promise<never>
ok_manual.Promise<never>.catch<void>(onrejected?: ((reason: any) => void | PromiseLike<void>) | null | undefined): Promise<void>
Attaches a callback for only the rejection of the Promise.
catch(() => {});
return {
ok_manual: Promise<never>
ok_manual,
ok_fetch: Promise<Response>
ok_fetch: fetch: (input: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response> (+1 overload)
fetch('/fetch/that/could/fail'),
dangerous_unhandled: Promise<never>
dangerous_unhandled: var Promise: PromiseConstructor
Represents the completion of an asynchronous operation
Promise.PromiseConstructor.reject<never>(reason?: any): Promise<never>
Creates a new rejected promise for the provided reason.
reject()
};
};
在不支援串流的平台上,例如 AWS Lambda 或 Firebase,回應將會被緩衝。這表示只有在所有 Promise 都解析後,頁面才會渲染。如果您正在使用代理(例如 NGINX),請確保它不會緩衝來自代理伺服器的回應。
只有在啟用 JavaScript 時,串流資料才會運作。如果頁面是伺服器端渲染的,您應該避免從通用
load
函式回傳 Promise,因為這些 Promise 不會被串流 — 相反地,當函式在瀏覽器中重新執行時,Promise 會被重新建立。
一旦回應開始串流,就無法變更回應的標頭和狀態碼,因此您無法在串流的 Promise 內
setHeaders
或拋出重新導向。
在 SvelteKit 1.x 中,頂層 Promise 會自動被等待,只有巢狀 Promise 會被串流。
平行載入
當渲染(或導航至)頁面時,SvelteKit 會同時執行所有 load
函式,避免請求的瀑布效應。在用戶端導航期間,呼叫多個伺服器端 load
函式的結果會被分組為單一回應。一旦所有 load
函式都回傳後,頁面就會被渲染。
重新執行 load 函式
SvelteKit 會追蹤每個 load
函式的依賴關係,以避免在導航期間不必要地重新執行它。
例如,給定一對像這樣的 load
函式...
import * as module "$lib/server/database"
db from '$lib/server/database';
/** @type {import('./$types').PageServerLoad} */
export async function function load(event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>): MaybePromise<void | Record<string, any>>
load({ params: Record<string, any>
The parameters of the current route - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params }) {
return {
post: {
title: string;
content: string;
}
post: await module "$lib/server/database"
db.function getPost(slug: string): Promise<{
title: string;
content: string;
}>
getPost(params: Record<string, any>
The parameters of the current route - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params.slug)
};
}
import * as module "$lib/server/database"
db from '$lib/server/database';
import type { type PageServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageServerLoad } from './$types';
export const const load: PageServerLoad
load: type PageServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageServerLoad = async ({ params: Record<string, any>
The parameters of the current route - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params }) => {
return {
post: {
title: string;
content: string;
}
post: await module "$lib/server/database"
db.function getPost(slug: string): Promise<{
title: string;
content: string;
}>
getPost(params: Record<string, any>
The parameters of the current route - e.g. for a route like /blog/[slug]
, a { slug: string }
object
params.slug)
};
};
import * as module "$lib/server/database"
db from '$lib/server/database';
/** @type {import('./$types').LayoutServerLoad} */
export async function function load(event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>): MaybePromise<void | Record<string, any>>
load() {
return {
posts: {
title: string;
slug: string;
}[]
posts: await module "$lib/server/database"
db.function getPostSummaries(): Promise<Array<{
title: string;
slug: string;
}>>
getPostSummaries()
};
}
import * as module "$lib/server/database"
db from '$lib/server/database';
import type { type LayoutServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
LayoutServerLoad } from './$types';
export const const load: LayoutServerLoad
load: type LayoutServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
LayoutServerLoad = async () => {
return {
posts: {
title: string;
slug: string;
}[]
posts: await module "$lib/server/database"
db.function getPostSummaries(): Promise<Array<{
title: string;
slug: string;
}>>
getPostSummaries()
};
};
...如果我們從 /blog/trying-the-raw-meat-diet
導航到 /blog/i-regret-my-choices
,則 +page.server.js
中的函式將會重新執行,因為 params.slug
已變更。+layout.server.js
中的函式不會重新執行,因為資料仍然有效。換句話說,我們不會第二次呼叫 db.getPostSummaries()
。
如果父層 load
函式被重新執行,則呼叫 await parent()
的 load
函式也會重新執行。
依賴關係追蹤不適用於 load
函式回傳之後 — 例如,在巢狀 Promise 內存取 params.x
不會導致函式在 params.x
變更時重新執行。(別擔心,如果您不小心這樣做,您會在開發中收到警告。)相反地,請在 load
函式的主要區塊中存取參數。
搜尋參數的追蹤與 URL 的其餘部分是獨立的。例如,在 load
函式內存取 event.url.searchParams.get("x")
會導致該 load
函式在從 ?x=1
導航到 ?x=2
時重新執行,但在從 ?x=1&y=1
導航到 ?x=1&y=2
時不會重新執行。
取消追蹤依賴關係
在極少數情況下,您可能希望從依賴關係追蹤機制中排除某些內容。您可以使用提供的 untrack
函式來執行此操作
/** @type {import('./$types').PageLoad} */
export async function function load({ untrack, url }: {
untrack: any;
url: any;
}): Promise<{
message: string;
} | undefined>
load({ untrack: any
untrack, url: any
url }) {
// Untrack url.pathname so that path changes don't trigger a rerun
if (untrack: any
untrack(() => url: any
url.pathname === '/')) {
return { message: string
message: 'Welcome!' };
}
}
import type { type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad } from './$types';
export const const load: PageLoad
load: type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad = async ({ untrack: <T>(fn: () => T) => T
Use this function to opt out of dependency tracking for everything that is synchronously called within the callback. Example:
src/routes/+page.serverexport async function load({ untrack, url }) {
// Untrack url.pathname so that path changes don't trigger a rerun
if (untrack(() => url.pathname === '/')) {
return { message: 'Welcome!' };
}
}
untrack, url: URL
The URL of the current page
url }) => {
// Untrack url.pathname so that path changes don't trigger a rerun
if (untrack: <boolean>(fn: () => boolean) => boolean
Use this function to opt out of dependency tracking for everything that is synchronously called within the callback. Example:
src/routes/+page.serverexport async function load({ untrack, url }) {
// Untrack url.pathname so that path changes don't trigger a rerun
if (untrack(() => url.pathname === '/')) {
return { message: 'Welcome!' };
}
}
untrack(() => url: URL
The URL of the current page
url.URL.pathname: string
pathname === '/')) {
return { message: string
message: 'Welcome!' };
}
};
手動失效
您也可以使用 invalidate(url)
重新執行適用於目前頁面的 load
函式,該函式會重新執行所有依賴於 url
的 load
函式,以及使用 invalidateAll()
重新執行每個 load
函式。伺服器端 load 函式永遠不會自動依賴於擷取的 url
,以避免將機密洩露給用戶端。
如果 load
函式呼叫 fetch(url)
或 depends(url)
,則該函式會依賴於 url
。請注意,url
可以是自訂的識別符號,開頭為 [a-z]:
/** @type {import('./$types').PageLoad} */
export async function function load({ fetch, depends }: {
fetch: any;
depends: any;
}): Promise<{
number: any;
}>
load({ fetch: any
fetch, depends: any
depends }) {
// load reruns when `invalidate('https://api.example.com/random-number')` is called...
const const response: any
response = await fetch: any
fetch('https://api.example.com/random-number');
// ...or when `invalidate('app:random')` is called
depends: any
depends('app:random');
return {
number: any
number: await const response: any
response.json()
};
}
import type { type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad } from './$types';
export const const load: PageLoad
load: type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad = async ({ fetch: {
(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>;
}
fetch
is equivalent to the native fetch
web API, with a few additional features:
- It can be used to make credentialed requests on the server, as it inherits the
cookie
and authorization
headers for the page request.
- It can make relative requests on the server (ordinarily,
fetch
requires a URL with an origin when used in a server context).
- Internal requests (e.g. for
+server.js
routes) go directly to the handler function when running on the server, without the overhead of an HTTP call.
- During server-side rendering, the response will be captured and inlined into the rendered HTML by hooking into the
text
and json
methods of the Response
object. Note that headers will not be serialized, unless explicitly included via filterSerializedResponseHeaders
- During hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request.
You can learn more about making credentialed requests with cookies here
fetch, depends: (...deps: Array<`${string}:${string}`>) => void
This function declares that the load
function has a dependency on one or more URLs or custom identifiers, which can subsequently be used with invalidate()
to cause load
to rerun.
Most of the time you won’t need this, as fetch
calls depends
on your behalf — it’s only necessary if you’re using a custom API client that bypasses fetch
.
URLs can be absolute or relative to the page being loaded, and must be encoded.
Custom identifiers have to be prefixed with one or more lowercase letters followed by a colon to conform to the URI specification.
The following example shows how to use depends
to register a dependency on a custom identifier, which is invalidate
d after a button click, making the load
function rerun.
src/routes/+pagelet count = 0;
export async function load({ depends }) {
depends('increase:count');
return { count: count++ };
}
src/routes/+page<script>
import { invalidate } from '$app/navigation';
let { data } = $props();
const increase = async () => {
await invalidate('increase:count');
}
</script>
<p>{data.count}<p>
<button on:click={increase}>Increase Count</button>
depends }) => {
// load reruns when `invalidate('https://api.example.com/random-number')` is called...
const const response: Response
response = await fetch: (input: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response> (+1 overload)
fetch('https://api.example.com/random-number');
// ...or when `invalidate('app:random')` is called
depends: (...deps: Array<`${string}:${string}`>) => void
This function declares that the load
function has a dependency on one or more URLs or custom identifiers, which can subsequently be used with invalidate()
to cause load
to rerun.
Most of the time you won’t need this, as fetch
calls depends
on your behalf — it’s only necessary if you’re using a custom API client that bypasses fetch
.
URLs can be absolute or relative to the page being loaded, and must be encoded.
Custom identifiers have to be prefixed with one or more lowercase letters followed by a colon to conform to the URI specification.
The following example shows how to use depends
to register a dependency on a custom identifier, which is invalidate
d after a button click, making the load
function rerun.
src/routes/+pagelet count = 0;
export async function load({ depends }) {
depends('increase:count');
return { count: count++ };
}
src/routes/+page<script>
import { invalidate } from '$app/navigation';
let { data } = $props();
const increase = async () => {
await invalidate('increase:count');
}
</script>
<p>{data.count}<p>
<button on:click={increase}>Increase Count</button>
depends('app:random');
return {
number: any
number: await const response: Response
response.Body.json(): Promise<any>
json()
};
};
<script>
import { invalidate, invalidateAll } from '$app/navigation';
/** @type {{ data: import('./$types').PageData }} */
let { data } = $props();
function rerunLoadFunction() {
// any of these will cause the `load` function to rerun
invalidate('app:random');
invalidate('https://api.example.com/random-number');
invalidate(url => url.href.includes('random-number'));
invalidateAll();
}
</script>
<p>random number: {data.number}</p>
<button onclick={rerunLoadFunction}>Update random number</button>
<script lang="ts">
import { invalidate, invalidateAll } from '$app/navigation';
import type { PageData } from './$types';
let { data }: { data: PageData } = $props();
function rerunLoadFunction() {
// any of these will cause the `load` function to rerun
invalidate('app:random');
invalidate('https://api.example.com/random-number');
invalidate(url => url.href.includes('random-number'));
invalidateAll();
}
</script>
<p>random number: {data.number}</p>
<button onclick={rerunLoadFunction}>Update random number</button>
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
函式已重新執行 - 子層
load
函式呼叫await parent()
並正在重新執行,且父層是伺服器端 load 函式 - 它透過
fetch
(僅限通用 load)或depends
宣告了對特定 URL 的依賴,且該 URL 已使用invalidate(url)
標記為失效 - 所有活動中的
load
函式都已使用invalidateAll()
強制重新執行
params
和 url
可以透過點擊 <a href="..">
連結、<form>
互動、goto
調用或 redirect
來變更。
請注意,重新執行 load
函式將會更新對應的 +layout.svelte
或 +page.svelte
內的 data
prop;它不會導致重新建立元件。因此,內部狀態會被保留。如果這不是您想要的,您可以在 afterNavigate
回呼中重設您需要重設的任何內容,和/或將您的元件包裝在 {#key ...}
區塊中。
驗證的意涵
載入資料的幾個功能對於驗證檢查具有重要的意涵
- 版面配置
load
函式不會在每次請求時執行,例如在子路由之間的用戶端導航期間。 (load 函式何時重新執行?) - 除非呼叫
await parent()
,否則版面配置和頁面load
函式會同時執行。如果版面配置load
拋出錯誤,頁面load
函式會執行,但用戶端不會收到回傳的資料。
有幾種可能的策略可確保在受保護程式碼之前執行驗證檢查。
為了防止資料瀑布並保留版面配置 load
快取
- 在任何
load
函式執行之前,使用 hook 來保護多個路由 - 直接在
+page.server.js
load
函式中使用驗證守衛來進行特定於路由的保護
在 +layout.server.js
中設置身份驗證守衛 (auth guard) 會要求所有子頁面在受保護的程式碼之前呼叫 await parent()
。除非每個子頁面都依賴 await parent()
返回的資料,否則其他選項會更有效能。