Skip to main content

加载数据

+page.svelte 组件(及其包含的 +layout.svelte 组件)渲染之前,我们通常需要获取一些数据。这是通过定义 load 函数来完成的。

¥Before a +page.svelte component (and its containing +layout.svelte components) can be rendered, we often need to get some data. This is done by defining load functions.

页面数据(Page data)

¥Page data

+page.svelte 文件可以有一个兄弟 +page.js,它导出一个 load 函数,该函数的返回值可通过 data prop 提供给页面:

¥A +page.svelte file can have a sibling +page.js that exports a load function, the return value of which is available to the page via the data prop:

src/routes/blog/[slug]/+page
/** @type {import('./$types').PageLoad} */
export function 
function load({ params }: {
    params: any;
}): {
    post: {
        title: string;
        content: string;
    };
}
@type{import('./$types').PageLoad}
load
({ params: anyparams }) {
return {
post: {
    title: string;
    content: string;
}
post
: {
title: stringtitle: `Title for ${params: anyparams.slug} goes here`, content: stringcontent: `Content for ${params: anyparams.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: PageLoadload:
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: stringtitle: `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: stringcontent: `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`
} }; };
src/routes/blog/[slug]/+page
<script>
	/** @type {import('./$types').PageProps} */
	let { data } = $props();
</script>

<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
<script lang="ts">
	import type { PageProps } from './$types';

	let { data }: PageProps = $props();
</script>

<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
Legacy mode

在 2.16.0 版本之前,页面和布局的属性必须单独输入:

¥[!LEGACY] Before version 2.16.0, the props of a page and layout had to be typed individually:

+page
/** @type {{ data: import('./$types').PageData }} */
let { let data: anydata } = function $props(): any

Declares the props that a component accepts. Example:

let { optionalProp = 42, requiredProp, bindableProp = $bindable() }: { optionalProp?: number; requiredProps: string; bindableProp: boolean } = $props();

https://svelte.dev/docs/svelte/$props

$props
();
import type { import PageDataPageData } from './$types';

let { let data: PageDatadata }: { data: PageDatadata: import PageDataPageData } = function $props(): any

Declares the props that a component accepts. Example:

let { optionalProp = 42, requiredProp, bindableProp = $bindable() }: { optionalProp?: number; requiredProps: string; bindableProp: boolean } = $props();

https://svelte.dev/docs/svelte/$props

$props
();

在 Svelte 4 中,你将使用 export let data

¥In Svelte 4, you’d use export let data instead.

多亏了生成的 $types 模块,我们获得了完全的类型安全。

¥Thanks to the generated $types module, we get full type safety.

+page.js 文件中的 load 函数在服务器和浏览器中运行(除非与 export const ssr = false 结合,在这种情况下它将 仅在浏览器中运行)。如果你的 load 函数应始终在服务器上运行(例如,因为它使用私有环境变量或访问数据库),那么它将改为进入 +page.server.js

¥A load function in a +page.js file runs both on the server and in the browser (unless combined with export const ssr = false, in which case it will only run in the browser). If your load function should always run on the server (because it uses private environment variables, for example, or accesses a database) then it would go in a +page.server.js instead.

你的博客文章的 load 函数的更现实版本仅在服务器上运行并从数据库中提取数据,可能如下所示:

¥A more realistic version of your blog post’s load function, that only runs on the server and pulls data from a database, might look like this:

src/routes/blog/[slug]/+page.server
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>>
@type{import('./$types').PageServerLoad}
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: PageServerLoadload: 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,请参阅 通用与服务器

¥Notice that the type changed from PageLoad to PageServerLoad, because server load functions can access additional arguments. To understand when to use +page.js and when to use +page.server.js, see Universal vs server.

布局数据(Layout data)

¥Layout data

你的 +layout.svelte 文件还可以通过 +layout.js+layout.server.js 加载数据。

¥Your +layout.svelte files can also load data, via +layout.js or +layout.server.js.

src/routes/blog/[slug]/+layout.server
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>>
@type{import('./$types').LayoutServerLoad}
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: LayoutServerLoadload: 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
()
}; };
src/routes/blog/[slug]/+layout
<script>
	/** @type {import('./$types').LayoutProps} */
	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 { LayoutProps } from './$types';

	let { data, children }: LayoutProps = $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>
Legacy mode

LayoutProps 是在 2.16.0 中添加的。在早期版本中,属性必须单独输入:

¥[!LEGACY] LayoutProps was added in 2.16.0. In earlier versions, properties had to be typed individually:

+layout
/** @type {{ data: import('./$types').LayoutData, children: Snippet }} */
let { let data: anydata, let children: anychildren } = function $props(): any

Declares the props that a component accepts. Example:

let { optionalProp = 42, requiredProp, bindableProp = $bindable() }: { optionalProp?: number; requiredProps: string; bindableProp: boolean } = $props();

https://svelte.dev/docs/svelte/$props

$props
();
import type { import LayoutDataLayoutData } from './$types';

let { let data: LayoutDatadata, let children: Snippetchildren }: { data: LayoutDatadata: import LayoutDataLayoutData, children: Snippetchildren: type Snippet = /*unresolved*/ anySnippet } = function $props(): any

Declares the props that a component accepts. Example:

let { optionalProp = 42, requiredProp, bindableProp = $bindable() }: { optionalProp?: number; requiredProps: string; bindableProp: boolean } = $props();

https://svelte.dev/docs/svelte/$props

$props
();

布局 load 函数返回的数据可用于子 +layout.svelte 组件和 +page.svelte 组件以及它 ‘belongs’ 到的布局。

¥Data returned from layout load functions is available to child +layout.svelte components and the +page.svelte component as well as the layout that it ‘belongs’ to.

src/routes/blog/[slug]/+page
<script>
	import { page } from '$app/state';

	/** @type {import('./$types').PageProps} */
	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/state';
	import type { PageProps } from './$types';

	let { data }: PageProps = $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 函数返回具有相同键的数据,则最后一个 ‘wins’ — 布局 load 返回 { a: 1, b: 2 } 和页面 load 返回 { b: 3, c: 4 } 的结果将是 { a: 1, b: 3, c: 4 }

¥[!NOTE] If multiple load functions return data with the same key, the last one ‘wins’ — the result of a layout load returning { a: 1, b: 2 } and a page load returning { b: 3, c: 4 } would be { a: 1, b: 3, c: 4 }.

page.data

+page.svelte 组件及其上方的每个 +layout.svelte 组件都可以访问自己的数据以及来自其父级的所有数据。

¥The +page.svelte component, and each +layout.svelte component above it, has access to its own data plus all the data from its parents.

在某些情况下,我们可能需要相反的情况 - 父布局可能需要访问页面数据或子布局中的数据。例如,根布局可能想要访问从 +page.js+page.server.js 中的 load 函数返回的 title 属性。这可以通过 page.data 完成:

¥In some cases, we might need the opposite — a parent layout might need to access page data or data from a child layout. For example, the root layout might want to access a title property returned from a load function in +page.js or +page.server.js. This can be done with page.data:

src/routes/+layout
<script>
	import { page } from '$app/state';
</script>

<svelte:head>
	<title>{page.data.title}</title>
</svelte:head>
<script lang="ts">
	import { page } from '$app/state';
</script>

<svelte:head>
	<title>{page.data.title}</title>
</svelte:head>

page.data 的类型信息由 App.PageData 提供。

¥Type information for page.data is provided by App.PageData.

Legacy mode

SvelteKit 2.12 中添加了 $app/state。如果你使用的是早期版本或使用 Svelte 4,请改用 $app/stores。它提供了一个 page 存储,具有你可以订阅的相同接口,例如 $page.data.title

¥[!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)

¥Universal vs server

正如我们所见,有两种类型的 load 函数:

¥As we’ve seen, there are two types of load function:

  • +page.js+layout.js 文件导出在服务器和浏览器中运行的通用 load 函数

    ¥+page.js and +layout.js files export universal load functions that run both on the server and in the browser

  • +page.server.js+layout.server.js 文件导出仅在服务器端运行的服务器 load 函数

    ¥+page.server.js and +layout.server.js files export server load functions that only run server-side

从概念上讲,它们是同一件事,但需要注意一些重要的区别。

¥Conceptually, they’re the same thing, but there are some important differences to be aware of.

哪个加载函数何时运行?(When does which load function run?)

¥When does which load function run?

服务器 load 函数始终在服务器上运行。

¥Server load functions always run on the server.

默认情况下,当用户首次访问你的页面时,通用 load 函数会在 SSR 期间在服务器上运行。然后它们将在水合期间再次运行,重用来自 获取请求 的任何响应。所有后续对通用 load 函数的调用都在浏览器中进行。你可以通过 页面选项 自定义行为。如果你禁用 服务器端渲染,你将获得一个 SPA,并且通用 load 函数始终在客户端上运行。

¥By default, universal load functions run on the server during SSR when the user first visits your page. They will then run again during hydration, reusing any responses from fetch requests. All subsequent invocations of universal load functions happen in the browser. You can customize the behavior through page options. If you disable server side rendering, you’ll get an SPA and universal load functions always run on the client.

如果路由包含通用和服务器 load 函数,则服务器 load 将首先运行。

¥If a route contains both universal and server load functions, the server load runs first.

load 函数在运行时被调用,除非你 prerender 页面 — 在这种情况下,它会在构建时被调用。

¥A load function is invoked at runtime, unless you prerender the page — in that case, it’s invoked at build time.

输入(Input)

¥Input

通用和服务器 load 函数都可以访问描述请求的属性(paramsrouteurl)和各种函数(fetchsetHeadersparentdependsuntrack)。这些将在以下部分中描述。

¥Both universal and server load functions have access to properties describing the request (params, route and url) and various functions (fetch, setHeaders, parent, depends and untrack). These are described in the following sections.

服务器 load 函数使用 ServerLoadEvent 调用,它从 RequestEvent 继承了 clientAddresscookieslocalsplatformrequest

¥Server load functions are called with a ServerLoadEvent, which inherits clientAddress, cookies, locals, platform and request from RequestEvent.

通用 load 函数使用具有 data 属性的 LoadEvent 调用。如果 +page.js+page.server.js(或 +layout.js+layout.server.js)中都有 load 函数,则服务器 load 函数的返回值是通用 load 函数参数的 data 属性。

¥Universal load functions are called with a LoadEvent, which has a data property. If you have load functions in both +page.js and +page.server.js (or +layout.js and +layout.server.js), the return value of the server load function is the data property of the universal load function’s argument.

输出(Output)

¥Output

通用 load 函数可以返回包含任何值的对象,包括自定义类和组件构造函数等。

¥A universal load function can return an object containing any values, including things like custom classes and component constructors.

服务器 load 函数必须返回可以用 devalue 序列化的数据(任何可以表示为 JSON 以及 BigIntDateMapSetRegExp 之类的东西,或重复/循环引用),以便可以通过网络传输。你的数据可以包含 promises,在这种情况下它将被流式传输到浏览器。

¥A server load function must return data that can be serialized with devalue — anything that can be represented as JSON plus things like BigInt, Date, Map, Set and RegExp, or repeated/cyclical references — so that it can be transported over the network. Your data can include promises, in which case it will be streamed to browsers.

何时使用哪个(When to use which)

¥When to use which

当你需要直接从数据库或文件系统访问数据,或者需要使用私有环境变量时,服务器 load 函数非常方便。

¥Server load functions are convenient when you need to access data directly from a database or filesystem, or need to use private environment variables.

当你需要从外部 API 获取 fetch 数据并且不需要私有凭据时,通用 load 函数非常有用,因为 SvelteKit 可以直接从 API 获取数据,而不是通过你的服务器。当你需要返回无法序列化的内容(例如 Svelte 组件构造函数)时,它们也很有用。

¥Universal load functions are useful when you need to fetch data from an external API and don’t need private credentials, since SvelteKit can get the data directly from the API rather than going via your server. They are also useful when you need to return something that can’t be serialized, such as a Svelte component constructor.

在极少数情况下,你可能需要同时使用两者 - 例如,你可能需要返回使用服务器数据初始化的自定义类的实例。同时使用两者时,服务器 load 返回值不会直接传递给页面,而是传递给通用 load 函数(作为 data 属性):

¥In rare cases, you might need to use both together — for example, you might need to return an instance of a custom class that was initialised with data from your server. When using both, the server load return value is not passed directly to the page, but to the universal load function (as the data property):

src/routes/+page.server
/** @type {import('./$types').PageServerLoad} */
export async function 
function load(): Promise<{
    serverMessage: string;
}>
@type{import('./$types').PageServerLoad}
load
() {
return { serverMessage: stringserverMessage: '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: PageServerLoadload:
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: stringserverMessage: 'hello from server load function' }; };
src/routes/+page
/** @type {import('./$types').PageLoad} */
export async function 
function load({ data }: {
    data: any;
}): Promise<{
    serverMessage: any;
    universalMessage: string;
}>
@type{import('./$types').PageLoad}
load
({ data: anydata }) {
return { serverMessage: anyserverMessage: data: anydata.serverMessage, universalMessage: stringuniversalMessage: '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: PageLoadload:
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: anyserverMessage: 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: stringuniversalMessage: 'hello from universal load function' }; };

使用 URL 数据(Using URL data)

¥Using URL data

通常,load 函数以某种方式依赖于 URL。为此,load 函数为你提供了 urlrouteparams

¥Often the load function depends on the URL in one way or another. For this, the load function provides you with url, route and params.

url

URL 的实例,包含 originhostnamepathnamesearchParams 等属性(包含解析后的查询字符串作为 URLSearchParams 对象)。url.hashload 期间无法访问,因为它在服务器上不可用。

¥An instance of URL, containing properties like the origin, hostname, pathname and searchParams (which contains the parsed query string as a URLSearchParams object). url.hash cannot be accessed during load, since it is unavailable on the server.

在某些环境中,这是从服务器端渲染期间的请求标头派生的。例如,如果你使用的是 adapter-node,则可能需要配置适配器以确保 URL 正确。

¥[!NOTE] In some environments this is derived from request headers during server-side rendering. If you’re using adapter-node, for example, you may need to configure the adapter in order for the URL to be correct.

route

包含当前路由目录的名称,相对于 src/routes

¥Contains the name of the current route directory, relative to src/routes:

src/routes/a/[b]/[...c]/+page
/** @type {import('./$types').PageLoad} */
export function 
function load({ route }: {
    route: any;
}): void
@type{import('./$types').PageLoad}
load
({ route: anyroute }) {
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
@seesource
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.

@sincev0.1.100
log
(route: anyroute.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: PageLoadload:
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
@seesource
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.

@sincev0.1.100
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.pathnameroute.id

¥params is derived from url.pathname and route.id.

假设 route.id/a/[b]/[...c]url.pathname/a/x/y/zparams 对象将如下所示:

¥Given a route.id of /a/[b]/[...c] and a url.pathname of /a/x/y/z, the params object would look like this:

{
	"b": "x",
	"c": "y/z"
}

发出获取请求(Making fetch requests)

¥Making fetch requests

要从外部 API 或 +server.js 处理程序获取数据,你可以使用提供的 fetch 函数,该函数的行为与 原生 fetch Web API 相同,但有一些附加功能:

¥To get data from an external API or a +server.js handler, you can use the provided fetch function, which behaves identically to the native fetch web API with a few additional features:

  • 它可用于在服务器上发出凭证请求,因为它继承了页面请求的 cookieauthorization 标头。

    ¥It can be used to make credentialed requests on the server, as it inherits the cookie and authorization headers for the page request.

  • 它可以在服务器上发出相对请求(通常,fetch 在服务器上下文中使用时需要具有来源的 URL)。

    ¥It can make relative requests on the server (ordinarily, fetch requires a URL with an origin when used in a server context).

  • 内部请求(例如,对于 +server.js 路由)在服务器上运行时直接转到处理程序函数,而无需 HTTP 调用的开销。

    ¥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.

  • 在服务器端渲染过程中,响应将通过挂接到 Response 对象的 textjsonarrayBuffer 方法中被捕获并内联到渲染的 HTML 中。请注意,除非通过 filterSerializedResponseHeaders 明确包含,否则不会序列化标头。

    ¥During server-side rendering, the response will be captured and inlined into the rendered HTML by hooking into the text, json and arrayBuffer methods of the Response object. Note that headers will not be serialized, unless explicitly included via filterSerializedResponseHeaders.

  • 在 hydration 期间,将从 HTML 中读取响应,以保证一致性并防止额外的网络请求 - 如果你在使用浏览器 fetch 而不是 load fetch 时在浏览器控制台中收到警告,这就是原因。

    ¥During hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request - if you received a warning in your browser console when using the browser fetch instead of the load fetch, this is why.

src/routes/items/[id]/+page
/** @type {import('./$types').PageLoad} */
export async function 
function load({ fetch, params }: {
    fetch: any;
    params: any;
}): Promise<{
    item: any;
}>
@type{import('./$types').PageLoad}
load
({ fetch: anyfetch, params: anyparams }) {
const const res: anyres = await fetch: anyfetch(`/api/items/${params: anyparams.id}`); const const item: anyitem = await const res: anyres.json(); return { item: anyitem }; }
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: PageLoadload:
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: Responseres = 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: anyitem = await const res: Responseres.Body.json(): Promise<any>json(); return { item: anyitem }; };

Cookies

服务器 load 函数可以获取和设置 cookies

¥A server load function can get and set cookies.

src/routes/+layout.server
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>>
@type{import('./$types').LayoutServerLoad}
load
({ cookies: Cookies

Get or set cookies related to the current request

cookies
}) {
const const sessionid: string | undefinedsessionid = 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.

@paramname the name of the cookie
@paramopts the options, passed directly to cookie.parse. See documentation here
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 | undefinedsessionid)
}; }
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: LayoutServerLoadload: 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 | undefinedsessionid = 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.

@paramname the name of the cookie
@paramopts the options, passed directly to cookie.parse. See documentation here
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 | undefinedsessionid)
}; };

只有当目标主机与 SvelteKit 应用或其更具体的子域相同时,Cookie 才会通过提供的 fetch 函数传递。

¥Cookies will only be passed through the provided fetch function if the target host is the same as the SvelteKit application or a more specific subdomain of it.

例如,如果 SvelteKit 正在为 my.domain.com 提供服务:

¥For example, if SvelteKit is serving my.domain.com:

  • domain.com 将不会收到 cookie

    ¥domain.com WILL NOT receive cookies

  • 其他属性:

    ¥my.domain.com WILL receive cookies

  • api.domain.com 将不会接收 cookie

    ¥api.domain.com WILL NOT receive cookies

  • 成功!

    ¥sub.my.domain.com WILL receive cookies

设置 credentials: 'include' 时不会传递其他 cookie,因为 SvelteKit 不知道哪个 cookie 属于哪个域(浏览器不会传递此信息),因此转发任何一个都不安全。使用 handleFetch 钩子 来解决它。

¥Other cookies will not be passed when credentials: 'include' is set, because SvelteKit does not know which domain which cookie belongs to (the browser does not pass this information along), so it’s not safe to forward any of them. Use the handleFetch hook to work around it.

标题(Headers)

¥Headers

服务器和通用 load 函数都可以访问 setHeaders 函数,当在服务器上运行时,它可以设置响应的标头。(在浏览器中运行时,setHeaders 无效。)如果你希望缓存页面,这将非常有用,例如:

¥Both server and universal load functions have access to a setHeaders function that, when running on the server, can set headers for the response. (When running in the browser, setHeaders has no effect.) This is useful if you want the page to be cached, for example:

src/routes/products/+page
/** @type {import('./$types').PageLoad} */
export async function 
function load({ fetch, setHeaders }: {
    fetch: any;
    setHeaders: any;
}): Promise<any>
@type{import('./$types').PageLoad}
load
({ fetch: anyfetch, setHeaders: anysetHeaders }) {
const const url: "https://cms.example.com/products.json"url = `https://cms.example.com/products.json`; const const response: anyresponse = await fetch: anyfetch(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: anysetHeaders({ age: anyage: const response: anyresponse.headers.get('age'), 'cache-control': const response: anyresponse.headers.get('cache-control') }); return const response: anyresponse.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: PageLoadload:
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/+page
export 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: Responseresponse = 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/+page
export 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 | nullage: const response: Responseresponse.Response.headers: Headersheaders.Headers.get(name: string): string | nullget('age'), 'cache-control': const response: Responseresponse.Response.headers: Headersheaders.Headers.get(name: string): string | nullget('cache-control') }); return const response: Responseresponse.Body.json(): Promise<any>json(); };

多次设置相同的标头(即使在单独的 load 函数中)是错误的。你只能使用 setHeaders 函数设置一次给定的标题。你不能使用 setHeaders 添加 set-cookie 标头 — 改用 cookies.set(name, value, options)

¥Setting the same header multiple times (even in separate load functions) is an error. You can only set a given header once using the setHeaders function. You cannot add a set-cookie header with setHeaders — use cookies.set(name, value, options) instead.

使用父级数据(Using parent data)

¥Using parent data

有时,load 函数从父 load 函数访问数据很有用,这可以通过 await parent() 完成:

¥Occasionally it’s useful for a load function to access data from a parent load function, which can be done with await parent():

src/routes/+layout
/** @type {import('./$types').LayoutLoad} */
export function 
function load(): {
    a: number;
}
@type{import('./$types').LayoutLoad}
load
() {
return { a: numbera: 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: LayoutLoadload:
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: numbera: 1 }; };
src/routes/abc/+layout
/** @type {import('./$types').LayoutLoad} */
export async function 
function load({ parent }: {
    parent: any;
}): Promise<{
    b: any;
}>
@type{import('./$types').LayoutLoad}
load
({ parent: anyparent }) {
const { const a: anya } = await parent: anyparent(); return { b: anyb: const a: anya + 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: LayoutLoadload:
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: anya } = 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: anyb: const a: anya + 1 }; };
src/routes/abc/+page
/** @type {import('./$types').PageLoad} */
export async function 
function load({ parent }: {
    parent: any;
}): Promise<{
    c: any;
}>
@type{import('./$types').PageLoad}
load
({ parent: anyparent }) {
const { const a: anya, const b: anyb } = await parent: anyparent(); return { c: anyc: const a: anya + const b: anyb }; }
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: PageLoadload:
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: anya, const b: anyb } = 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: anyc: const a: anya + const b: anyb }; };
src/routes/abc/+page
<script>
	/** @type {import('./$types').PageProps} */
	let { data } = $props();
</script>

<!-- renders `1 + 2 = 3` -->
<p>{data.a} + {data.b} = {data.c}</p>
<script lang="ts">
	import type { PageProps } from './$types';

	let { data }: PageProps = $props();
</script>

<!-- renders `1 + 2 = 3` -->
<p>{data.a} + {data.b} = {data.c}</p>

请注意,+page.js 中的 load 函数从两个布局 load 函数接收合并的数据,而不仅仅是直接父级。

¥[!NOTE] Notice that the load function in +page.js receives the merged data from both layout load functions, not just the immediate parent.

+page.server.js+layout.server.js 中,parent 从父 +layout.server.js 文件返回数据。

¥Inside +page.server.js and +layout.server.js, parent returns data from parent +layout.server.js files.

+page.js+layout.js 中,它将从父 +layout.js 文件返回数据。但是,缺少的 +layout.js 被视为 ({ data }) => data 函数,这意味着它还将从 +layout.js 文件的非 ‘shadowed’ 的父 +layout.server.js 文件返回数据

¥In +page.js or +layout.js it will return data from parent +layout.js files. However, a missing +layout.js is treated as a ({ data }) => data function, meaning that it will also return data from parent +layout.server.js files that are not ‘shadowed’ by a +layout.js file

使用 await parent() 时,请注意不要引入瀑布。例如,这里 getData(params) 不依赖于调用 parent() 的结果,因此我们应该先调用它以避免延迟渲染。

¥Take care not to introduce waterfalls when using await parent(). Here, for example, getData(params) does not depend on the result of calling parent(), so we should call it first to avoid a delayed render.

+page
/** @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>>
@type{import('./$types').PageLoad}
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: anymeta: { ...const parentData: Record<string, any>parentData.meta, ...
const data: {
    meta: any;
}
data
.meta: anymeta }
}; }
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: PageLoadload: 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: anymeta: { ...const parentData: Record<string, any>parentData.meta, ...
const data: {
    meta: any;
}
data
.meta: anymeta }
}; };

错误(Errors)

¥Errors

如果在 load 期间抛出错误,将渲染最近的 +error.svelte。对于 expected 错误,使用 @sveltejs/kit 中的 error 助手指定 HTTP 状态代码和可选消息:

¥If an error is thrown during load, the nearest +error.svelte will be rendered. For expected errors, use the error helper from @sveltejs/kit to specify the HTTP status code and an optional message:

src/routes/admin/+layout.server
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.

@paramstatus The HTTP status code. Must be in the range 400-599.
@parambody An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
@throwsHttpError This error instructs SvelteKit to initiate HTTP error handling.
@throwsError If the provided status is invalid (not between 400 and 599).
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>>
@type{import('./$types').LayoutServerLoad}
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.

@paramstatus The HTTP status code. Must be in the range 400-599.
@parambody An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
@throwsHttpError This error instructs SvelteKit to initiate HTTP error handling.
@throwsError If the provided status is invalid (not between 400 and 599).
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: booleanisAdmin) {
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.

@paramstatus The HTTP status code. Must be in the range 400-599.
@parambody An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
@throwsHttpError This error instructs SvelteKit to initiate HTTP error handling.
@throwsError If the provided status is invalid (not between 400 and 599).
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.

@paramstatus The HTTP status code. Must be in the range 400-599.
@parambody An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
@throwsHttpError This error instructs SvelteKit to initiate HTTP error handling.
@throwsError If the provided status is invalid (not between 400 and 599).
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: LayoutServerLoadload: 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.

@paramstatus The HTTP status code. Must be in the range 400-599.
@parambody An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
@throwsHttpError This error instructs SvelteKit to initiate HTTP error handling.
@throwsError If the provided status is invalid (not between 400 and 599).
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: booleanisAdmin) {
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.

@paramstatus The HTTP status code. Must be in the range 400-599.
@parambody An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
@throwsHttpError This error instructs SvelteKit to initiate HTTP error handling.
@throwsError If the provided status is invalid (not between 400 and 599).
error
(403, 'not an admin');
} };

调用 error(...) 将引发异常,从而可以轻松地从辅助函数内部停止执行。

¥Calling error(...) will throw an exception, making it easy to stop execution from inside helper functions.

如果抛出 unexpected 错误,SvelteKit 将调用 handleError 并将其视为 500 内部错误。

¥If an unexpected error is thrown, SvelteKit will invoke handleError and treat it as a 500 Internal Error.

在 SvelteKit 1.x 中 你必须自己 throw 错误

¥[!NOTE] In SvelteKit 1.x you had to throw the error yourself

重定向(Redirects)

¥Redirects

要重定向用户,请使用 @sveltejs/kit 中的 redirect 助手来指定应将他们重定向到的位置以及 3xx 状态代码。与 error(...) 一样,调用 redirect(...) 将引发异常,从而可以轻松地从辅助函数内部停止执行。

¥To redirect users, use the redirect helper from @sveltejs/kit to specify the location to which they should be redirected alongside a 3xx status code. Like error(...), calling redirect(...) will throw an exception, making it easy to stop execution from inside helper functions.

src/routes/user/+layout.server
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.

@paramstatus The HTTP status code. Must be in the range 300-308.
@paramlocation The location to redirect to.
@throwsRedirect This error instructs SvelteKit to redirect to the specified location.
@throwsError If the provided status is invalid.
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>>
@type{import('./$types').LayoutServerLoad}
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.

@paramstatus The HTTP status code. Must be in the range 300-308.
@paramlocation The location to redirect to.
@throwsRedirect This error instructs SvelteKit to redirect to the specified location.
@throwsError If the provided status is invalid.
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.

@paramstatus The HTTP status code. Must be in the range 300-308.
@paramlocation The location to redirect to.
@throwsRedirect This error instructs SvelteKit to redirect to the specified location.
@throwsError If the provided status is invalid.
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: LayoutServerLoadload: 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.

@paramstatus The HTTP status code. Must be in the range 300-308.
@paramlocation The location to redirect to.
@throwsRedirect This error instructs SvelteKit to redirect to the specified location.
@throwsError If the provided status is invalid.
redirect
(307, '/login');
} };

不要在 try {...} 块内使用 redirect(),因为重定向将立即触发 catch 语句。

¥[!NOTE] Don’t use redirect() inside a try {...} block, as the redirect will immediately trigger the catch statement.

在浏览器中,你还可以使用 $app.navigation 中的 goto 以编程方式在 load 函数之外导航。

¥In the browser, you can also navigate programmatically outside of a load function using goto from $app.navigation.

在 SvelteKit 1.x 中 你必须自己 throw redirect

¥[!NOTE] In SvelteKit 1.x you had to throw the redirect yourself

使用 promise 进行流式传输(Streaming with promises)

¥Streaming with promises

使用服务器 load 时,promise 将在解析时流式传输到浏览器。如果你拥有缓慢、非必要的数据,这将非常有用,因为你可以在所有数据可用之前开始渲染页面:

¥When using a server load, promises will be streamed to the browser as they resolve. This is useful if you have slow, non-essential data, since you can start rendering the page before all the data is available:

src/routes/blog/[slug]/+page.server
/** @type {import('./$types').PageServerLoad} */
export async function function load(event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>): MaybePromise<void | Record<string, any>>
@type{import('./$types').PageServerLoad}
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: PageServerLoadload: 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)
}; };

这对于创建骨架加载状态很有用,例如:

¥This is useful for creating skeleton loading states, for example:

src/routes/blog/[slug]/+page
<script>
	/** @type {import('./$types').PageProps} */
	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 { PageProps } from './$types';

	let { data }: PageProps = $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 以将其标记为已处理就足够了。

¥When streaming data, be careful to handle promise rejections correctly. More specifically, the server could crash with an “unhandled promise rejection” error if a lazy-loaded promise fails before rendering starts (at which point it’s caught) and isn’t handling the error in some way. When using SvelteKit’s fetch directly in the load function, SvelteKit will handle this case for you. For other promises, it is enough to attach a noop-catch to the promise to mark it as handled.

src/routes/+page.server
/** @type {import('./$types').PageServerLoad} */
export function 
function load({ fetch }: {
    fetch: any;
}): {
    ok_manual: Promise<never>;
    ok_fetch: any;
    dangerous_unhandled: Promise<never>;
}
@type{import('./$types').PageServerLoad}
load
({ fetch: anyfetch }) {
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.

@paramreason The reason the promise was rejected.
@returnsA new rejected Promise.
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.

@paramonrejected The callback to execute when the Promise is rejected.
@returnsA Promise for the completion of the callback.
catch
(() => {});
return { ok_manual: Promise<never>ok_manual, ok_fetch: anyok_fetch: fetch: anyfetch('/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.

@paramreason The reason the promise was rejected.
@returnsA new rejected Promise.
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: PageServerLoadload:
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.

@paramreason The reason the promise was rejected.
@returnsA new rejected Promise.
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.

@paramonrejected The callback to execute when the Promise is rejected.
@returnsA Promise for the completion of the callback.
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.

@paramreason The reason the promise was rejected.
@returnsA new rejected Promise.
reject
()
}; };

在 AWS Lambda 或 Firebase 等不支持流式传输的平台上,响应将被缓冲。这意味着页面只有在所有 promise 都解决后才会渲染。如果你使用的是代理(例如 NGINX),请确保它不会缓冲来自代理服务器的响应。

¥[!NOTE] On platforms that do not support streaming, such as AWS Lambda or Firebase, responses will be buffered. This means the page will only render once all promises resolve. If you are using a proxy (e.g. NGINX), make sure it does not buffer responses from the proxied server.

流数据仅在启用 JavaScript 时才有效。如果页面是服务器渲染的,你应该避免从通用 load 函数返回 promise,因为这些 promise 不是流式传输的 - 相反,当函数在浏览器中重新运行时会重新创建 promise。

¥[!NOTE] Streaming data will only work when JavaScript is enabled. You should avoid returning promises from a universal load function if the page is server rendered, as these are not streamed — instead, the promise is recreated when the function reruns in the browser.

一旦响应开始流式传输,响应的标头和状态代码就无法更改,因此你无法在流式传输的 promise 内 setHeaders 或抛出重定向。

¥[!NOTE] The headers and status code of a response cannot be changed once the response has started streaming, therefore you cannot setHeaders or throw redirects inside a streamed promise.

在 SvelteKit 1.x 中 顶层 promise 被自动等待,只有嵌套 promise 被流式传输。

¥[!NOTE] In SvelteKit 1.x top-level promises were automatically awaited, only nested promises were streamed.

并行加载(Parallel loading)

¥Parallel loading

当渲染(或导航到)页面时,SvelteKit 会同时运行所有 load 函数,避免大量请求。在客户端导航期间,调用多个服务器 load 函数的结果被分组为单个响应。一旦所有 load 函数都返回,页面就会被渲染。

¥When rendering (or navigating to) a page, SvelteKit runs all load functions concurrently, avoiding a waterfall of requests. During client-side navigation, the result of calling multiple server load functions are grouped into a single response. Once all load functions have returned, the page is rendered.

重新运行加载函数(Rerunning load functions)

¥Rerunning load functions

SvelteKit 跟踪每个 load 函数的依赖,以避免在导航期间不必要地重新运行它。

¥SvelteKit tracks the dependencies of each load function to avoid rerunning it unnecessarily during navigation.

例如,给定一对这样的 load 函数...

¥For example, given a pair of load functions like these...

src/routes/blog/[slug]/+page.server
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>>
@type{import('./$types').PageServerLoad}
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: PageServerLoadload: 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)
}; };
src/routes/blog/[slug]/+layout.server
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>>
@type{import('./$types').LayoutServerLoad}
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: LayoutServerLoadload: 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()

¥...the one in +page.server.js will rerun if we navigate from /blog/trying-the-raw-meat-diet to /blog/i-regret-my-choices because params.slug has changed. The one in +layout.server.js will not, because the data is still valid. In other words, we won’t call db.getPostSummaries() a second time.

如果父 load 函数重新运行,调用 await parent()load 函数也会重新运行。

¥A load function that calls await parent() will also rerun if a parent load function is rerun.

load 函数返回后,依赖跟踪不适用 - 例如,在嵌套的 promise 中访问 params.x 不会导致函数在 params.x 更改时重新运行。(别担心,如果你不小心这样做了,你会在开发中收到警告。)相反,在 load 函数的主体中访问参数。

¥Dependency tracking does not apply after the load function has returned — for example, accessing params.x inside a nested promise will not cause the function to rerun when params.x changes. (Don’t worry, you’ll get a warning in development if you accidentally do this.) Instead, access the parameter in the main body of your load function.

搜索参数独立于其余 URL 进行跟踪。例如,在 load 函数内访问 event.url.searchParams.get("x") 将使 load 函数在从 ?x=1 导航到 ?x=2 时重新运行,但从 ?x=1&y=1 导航到 ?x=1&y=2 时不会重新运行。

¥Search parameters are tracked independently from the rest of the url. For example, accessing event.url.searchParams.get("x") inside a load function will make that load function re-run when navigating from ?x=1 to ?x=2, but not when navigating from ?x=1&y=1 to ?x=1&y=2.

取消跟踪依赖(Untracking dependencies)

¥Untracking dependencies

在极少数情况下,你可能希望从依赖跟踪机制中排除某些内容。你可以使用提供的 untrack 函数执行此操作:

¥In rare cases, you may wish to exclude something from the dependency tracking mechanism. You can do this with the provided untrack function:

src/routes/+page
/** @type {import('./$types').PageLoad} */
export async function 
function load({ untrack, url }: {
    untrack: any;
    url: any;
}): Promise<{
    message: string;
} | undefined>
@type{import('./$types').PageLoad}
load
({ untrack: anyuntrack, url: anyurl }) {
// Untrack url.pathname so that path changes don't trigger a rerun if (untrack: anyuntrack(() => url: anyurl.pathname === '/')) { return { message: stringmessage: '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: PageLoadload:
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.server
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!' };
	}
}
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.server
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!' };
	}
}
untrack
(() => url: URL

The URL of the current page

url
.URL.pathname: stringpathname === '/')) {
return { message: stringmessage: 'Welcome!' }; } };

手动失效(Manual invalidation)

¥Manual invalidation

你还可以使用 invalidate(url) 重新运行适用于当前页面的 load 函数,这会重新运行依赖于 url 的所有 load 函数,以及 invalidateAll(),这会重新运行每个 load 函数。服务器加载函数永远不会自动依赖于获取的 url,以避免将密钥泄露给客户端。

¥You can also rerun load functions that apply to the current page using invalidate(url), which reruns all load functions that depend on url, and invalidateAll(), which reruns every load function. Server load functions will never automatically depend on a fetched url to avoid leaking secrets to the client.

如果 load 函数调用 fetch(url)depends(url),则它依赖于 url。请注意,url 可以是以 [a-z]: 开头的自定义标识符:

¥A load function depends on url if it calls fetch(url) or depends(url). Note that url can be a custom identifier that starts with [a-z]::

src/routes/random-number/+page
/** @type {import('./$types').PageLoad} */
export async function 
function load({ fetch, depends }: {
    fetch: any;
    depends: any;
}): Promise<{
    number: any;
}>
@type{import('./$types').PageLoad}
load
({ fetch: anyfetch, depends: anydepends }) {
// load reruns when `invalidate('https://api.example.com/random-number')` is called... const const response: anyresponse = await fetch: anyfetch('https://api.example.com/random-number'); // ...or when `invalidate('app:random')` is called depends: anydepends('app:random'); return { number: anynumber: await const response: anyresponse.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: PageLoadload:
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 invalidated after a button click, making the load function rerun.

src/routes/+page
let count = 0;
export async function load({ depends }) {
	depends('increase:count');

	return { count: count++ };
}
src/routes/+page
&#x3C;script>
	import { invalidate } from '$app/navigation';

	let { data } = $props();

	const increase = async () => {
		await invalidate('increase:count');
	}
&#x3C;/script>

&#x3C;p>{data.count}&#x3C;p>
&#x3C;button on:click={increase}>Increase Count&#x3C;/button>
depends
}) => {
// load reruns when `invalidate('https://api.example.com/random-number')` is called... const const response: Responseresponse = 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 invalidated after a button click, making the load function rerun.

src/routes/+page
let count = 0;
export async function load({ depends }) {
	depends('increase:count');

	return { count: count++ };
}
src/routes/+page
&#x3C;script>
	import { invalidate } from '$app/navigation';

	let { data } = $props();

	const increase = async () => {
		await invalidate('increase:count');
	}
&#x3C;/script>

&#x3C;p>{data.count}&#x3C;p>
&#x3C;button on:click={increase}>Increase Count&#x3C;/button>
depends
('app:random');
return { number: anynumber: await const response: Responseresponse.Body.json(): Promise<any>json() }; };
src/routes/random-number/+page
<script>
	import { invalidate, invalidateAll } from '$app/navigation';

	/** @type {import('./$types').PageProps} */
	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 { PageProps } from './$types';

	let { data }: PageProps = $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>

加载函数何时重新运行?(When do load functions rerun?)

¥When do load functions rerun?

总而言之,load 函数将在以下情况下重新运行:

¥To summarize, a load function will rerun in the following situations:

  • 它引用 params 的属性,其值已更改

    ¥It references a property of params whose value has changed

  • 它引用 url 的属性(例如 url.pathnameurl.search),其值已更改。request.url 中的属性未被跟踪

    ¥It references a property of url (such as url.pathname or url.search) whose value has changed. Properties in request.url are not tracked

  • 它调用 url.searchParams.get(...)url.searchParams.getAll(...)url.searchParams.has(...),并且相关参数会发生变化。访问 url.searchParams 的其他属性将具有与访问 url.search 相同的效果。

    ¥It calls url.searchParams.get(...), url.searchParams.getAll(...) or url.searchParams.has(...) and the parameter in question changes. Accessing other properties of url.searchParams will have the same effect as accessing url.search.

  • 它调用 await parent() 并重新运行父 load 函数

    ¥It calls await parent() and a parent load function reran

  • load 函数调用 await parent() 并重新运行,父函数是服务器加载函数

    ¥A child load function calls await parent() and is rerunning, and the parent is a server load function

  • 它通过 fetch(仅通用加载)或 depends 声明对特定 URL 的依赖,并且该 URL 被 invalidate(url) 标记为无效

    ¥It declared a dependency on a specific URL via fetch (universal load only) or depends, and that URL was marked invalid with invalidate(url)

  • 所有活动的 load 函数都强制使用 invalidateAll() 重新运行

    ¥All active load functions were forcibly rerun with invalidateAll()

paramsurl 可以响应 <a href=".."> 链接点击、<form> 交互goto 调用或 redirect 而改变。

¥params and url can change in response to a <a href=".."> link click, a <form> interaction, a goto invocation, or a redirect.

请注意,重新运行 load 函数将更新相应 +layout.svelte+page.svelte 内的 data prop;不会导致重新创建组件。因此,内部状态得以保留。如果这不是你想要的,你可以在 afterNavigate 回调中重置你需要重置的任何内容,和/或将你的组件封装在 {#key ...} 块中。

¥Note that rerunning a load function will update the data prop inside the corresponding +layout.svelte or +page.svelte; it does not cause the component to be recreated. As a result, internal state is preserved. If this isn’t what you want, you can reset whatever you need to reset inside an afterNavigate callback, and/or wrap your component in a {#key ...} block.

身份验证的含义(Implications for authentication)

¥Implications for authentication

加载数据的几个功能对身份验证检查具有重要影响:

¥A couple features of loading data have important implications for auth checks:

  • Layout load 函数不会在每个请求上运行,例如在子路由之间的客户端导航期间。(何时执行加载函数重新运行?)

    ¥Layout load functions do not run on every request, such as during client side navigation between child routes. (When do load functions rerun?)

  • 除非调用 await parent(),否则布局和页面 load 函数会同时运行。如果布局 load 抛出,页面 load 函数将运行,但客户端不会收到返回的数据。

    ¥Layout and page load functions run concurrently unless await parent() is called. If a layout load throws, the page load function runs, but the client will not receive the returned data.

有几种可能的策略可以确保在受保护的代码之前进行身份验证检查。

¥There are a few possible strategies to ensure an auth check occurs before protected code.

为了防止数据瀑布并保留布局 load 缓存:

¥To prevent data waterfalls and preserve layout load caches:

  • 在运行任何 load 函数之前,使用 hooks 保护多条路由

    ¥Use hooks to protect multiple routes before any load functions run

  • +page.server.js load 函数中直接使用身份验证保护器进行特定路由保护

    ¥Use auth guards directly in +page.server.js load functions for route specific protection

+layout.server.js 中放置身份验证保护需要所有子页面在受保护的代码之前调用 await parent()。除非每个子页面都依赖于从 await parent() 返回的数据,否则其他选项的性能会更高。

¥Putting an auth guard in +layout.server.js requires all child pages to call await parent() before protected code. Unless every child page depends on returned data from await parent(), the other options will be more performant.

进一步阅读(Further reading)

¥Further reading

上一页 下一页