核心概念
加载数据
在 GitHub 上编辑此页面在 +page.svelte
组件(及其包含的 +layout.svelte
组件)可以被渲染之前,我们通常需要获取一些数据。这是通过定义 load
函数来完成的。
页面数据永久链接
+page.svelte
文件可以有一个兄弟 +page.js
文件,它导出一个 load
函数,其返回值通过 data
prop 提供给页面
ts
/** @type {import('./$types').PageLoad} */export functionload ({params }) {return {post : {title : `Title for ${params .slug } goes here`,content : `Content for ${params .slug } goes here`}};}
ts
import type {PageLoad } from './$types';export constload :PageLoad = ({params }) => {return {post : {title : `Title for ${params .slug } goes here`,content : `Content for ${params .slug } goes here`,},};};
<script>
/** @type {import('./$types').PageData} */
export let data;
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
感谢生成的 $types
模块,我们获得了完全类型安全性。
+page.js
文件中的 load
函数在服务器和浏览器中同时运行(除非与 export const ssr = false
结合使用,在这种情况下它将 仅在浏览器中运行)。如果你的 load
函数应该始终在服务器上运行(例如,因为它使用私有环境变量或访问数据库),那么它应该放在 +page.server.js
中。
你的博客文章的 load
函数的一个更实际的版本,它仅在服务器上运行并从数据库中提取数据,可能如下所示
ts
import * asdb from '$lib/server/database';/** @type {import('./$types').PageServerLoad} */export async functionload ({params }) {return {post : awaitdb .getPost (params .slug )};}
ts
import * asdb from '$lib/server/database';import type {PageServerLoad } from './$types';export constload :PageServerLoad = async ({params }) => {return {post : awaitdb .getPost (params .slug ),};};
请注意,类型已从 PageLoad
更改为 PageServerLoad
,因为服务器 load
函数可以访问其他参数。要了解何时使用 +page.js
以及何时使用 +page.server.js
,请参阅 通用与服务器。
布局数据永久链接
您的 +layout.svelte
文件也可以通过 +layout.js
或 +layout.server.js
加载数据。
ts
import * asdb from '$lib/server/database';/** @type {import('./$types').LayoutServerLoad} */export async functionload () {return {posts : awaitdb .getPostSummaries ()};}
ts
import * asdb from '$lib/server/database';import type {LayoutServerLoad } from './$types';export constload :LayoutServerLoad = async () => {return {posts : awaitdb .getPostSummaries (),};};
<script>
/** @type {import('./$types').LayoutData} */
export let data;
</script>
<main>
<!-- +page.svelte is rendered in this <slot> -->
<slot />
</main>
<aside>
<h2>More posts</h2>
<ul>
{#each data.posts as post}
<li>
<a href="/blog/{post.slug}">
{post.title}
</a>
</li>
{/each}
</ul>
</aside>
<script lang="ts">
import type { LayoutData } from './$types';
export let data: LayoutData;
</script>
<main>
<!-- +page.svelte is rendered in this <slot> -->
<slot />
</main>
<aside>
<h2>More posts</h2>
<ul>
{#each data.posts as post}
<li>
<a href="/blog/{post.slug}">
{post.title}
</a>
</li>
{/each}
</ul>
</aside>
从布局 load
函数返回的数据可供子 +layout.svelte
组件和 +page.svelte
组件以及它“所属”的布局使用。
<script>
import { page } from '$app/stores';
/** @type {import('./$types').PageData} */
export let data;
// we can access `data.posts` because it's returned from
// the parent layout `load` function
$: index = data.posts.findIndex(post => post.slug === $page.params.slug);
$: next = data.posts[index - 1];
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
{#if next}
<p>Next post: <a href="/blog/{next.slug}">{next.title}</a></p>
{/if}
如果多个
load
函数返回具有相同键的数据,则最后一个“获胜”——布局load
返回{ a: 1, b: 2 }
和页面load
返回{ b: 3, c: 4 }
的结果将是{ a: 1, b: 3, c: 4 }
。
$page.data永久链接
+page.svelte
组件及其上方的每个 +layout.svelte
组件都可以访问其自身数据以及其父组件的所有数据。
在某些情况下,我们可能需要相反的情况——父布局可能需要访问页面数据或子布局中的数据。例如,根布局可能希望访问 +page.js
或 +page.server.js
中的 load
函数返回的 title
属性。这可以通过 $page.data
完成
<script>
import { page } from '$app/stores';
</script>
<svelte:head>
<title>{$page.data.title}</title>
</svelte:head>
<script lang="ts">
import { page } from '$app/stores';
</script>
<svelte:head>
<title>{$page.data.title}</title>
</svelte:head>
$page.data
的类型信息由 App.PageData
提供。
通用与服务器永久链接
正如我们所见,有两种类型的 load
函数
+page.js
和+layout.js
文件导出在服务器和浏览器上都运行的通用load
函数+page.server.js
和+layout.server.js
文件导出仅在服务器端运行的服务器load
函数
从概念上讲,它们是同一件事,但有一些重要的区别需要注意。
哪个加载函数在何时运行?永久链接
服务器load
函数始终在服务器上运行。
默认情况下,当用户首次访问你的页面时,通用load
函数在服务器端渲染 (SSR) 期间运行。它们将在水化期间再次运行,重复使用来自fetch 请求的任何响应。通用load
函数的所有后续调用都在浏览器中发生。你可以通过页面选项自定义行为。如果你禁用了服务器端渲染,你将获得一个 SPA,并且通用load
函数始终在客户端运行。
如果一个路由同时包含通用和服务器load
函数,则服务器load
函数首先运行。
除非你预渲染页面,否则load
函数在运行时被调用——在这种情况下,它在构建时被调用。
输入永久链接
通用和服务器load
函数都可以访问描述请求的属性(params
、route
和url
)和各种函数(fetch
、setHeaders
、parent
、depends
和untrack
)。这些将在以下部分中进行描述。
服务器load
函数使用ServerLoadEvent
调用,它从RequestEvent
继承了clientAddress
、cookies
、locals
、platform
和request
。
通用load
函数使用LoadEvent
调用,它有一个data
属性。如果你在+page.js
和+page.server.js
(或+layout.js
和+layout.server.js
)中都有load
函数,则服务器load
函数的返回值是通用load
函数参数的data
属性。
输出永久链接
通用load
函数可以返回一个包含任何值的对象,包括自定义类和组件构造函数等内容。
服务器load
函数必须返回可以用devalue序列化的数据——任何可以表示为 JSON 的内容以及BigInt
、Date
、Map
、Set
和RegExp
等内容,或重复/循环引用——以便它可以通过网络传输。你的数据可以包括promise,在这种情况下,它将流式传输到浏览器。
何时使用哪个永久链接
当你需要直接从数据库或文件系统访问数据,或需要使用私有环境变量时,服务器load
函数很方便。
通用load
函数在你需要从外部 API fetch
数据并且不需要私有凭据时很有用,因为 SvelteKit 可以直接从 API 获取数据,而不是通过你的服务器。当你需要返回无法序列化的内容(例如 Svelte 组件构造函数)时,它们也很有用。
在极少数情况下,您可能需要同时使用两者 — 例如,您可能需要返回一个自定义类的实例,该实例已使用来自服务器的数据进行初始化。同时使用时,服务器 load
返回值不会直接传递到页面,而是传递到通用 load
函数(作为 data
属性)
ts
/** @type {import('./$types').PageServerLoad} */export async functionload () {return {serverMessage : 'hello from server load function'};}
ts
import type {PageServerLoad } from './$types';export constload :PageServerLoad = async () => {return {serverMessage : 'hello from server load function',};};
ts
/** @type {import('./$types').PageLoad} */export async functionload ({data }) {return {serverMessage :data .serverMessage ,universalMessage : 'hello from universal load function'};}
ts
import type {PageLoad } from './$types';export constload :PageLoad = async ({data }) => {return {serverMessage :data .serverMessage ,universalMessage : 'hello from universal load function',};};
使用 URL 数据永久链接
通常,load
函数以某种方式依赖于 URL。为此,load
函数为您提供了 url
、route
和 params
。
url永久链接
URL
的实例,包含诸如 origin
、hostname
、pathname
和 searchParams
(包含作为 URLSearchParams
对象解析后的查询字符串)之类的属性。在 load
期间无法访问 url.hash
,因为它在服务器上不可用。
在某些环境中,这是在服务器端渲染期间从请求头派生的。例如,如果您正在使用
adapter-node
,您可能需要配置适配器才能使 URL 正确。
route永久链接
包含相对于 src/routes
的当前路由目录的名称
ts
/** @type {import('./$types').PageLoad} */export functionload ({route }) {console .log (route .id ); // '/a/[b]/[...c]'}
ts
import type {PageLoad } from './$types';export constload :PageLoad = ({route }) => {console .log (route .id ); // '/a/[b]/[...c]'};
params永久链接
params
派生自 url.pathname
和 route.id
。
给定 route.id
为 /a/[b]/[...c]
和 url.pathname
为 /a/x/y/z
,params
对象将如下所示
ts
{"b": "x","c": "y/z"}
发出获取请求永久链接
要从外部 API 或 +server.js
处理程序获取数据,您可以使用提供的 fetch
函数,其行为与 native fetch
web API 完全相同,但具有以下一些附加功能
- 它可用于对服务器进行凭据请求,因为它继承了页面请求的
cookie
和authorization
标头。 - 它可以在服务器上进行相对请求(通常,
fetch
在服务器上下文中使用时需要带有原点的 URL)。 - 内部请求(例如对于
+server.js
路由)在服务器上运行时直接转到处理程序函数,而无需 HTTP 调用的开销。 - 在服务器端渲染期间,将通过挂接到
Response
对象的text
、json
和arrayBuffer
方法来捕获响应并将其内联到渲染的 HTML 中。请注意,标头将不会被序列化,除非通过filterSerializedResponseHeaders
明确包含。 - 在水化期间,将从 HTML 中读取响应,从而保证一致性并防止额外的网络请求 - 如果你在使用浏览器的
fetch
而不是load
fetch
时在浏览器控制台中收到警告,这就是原因。
ts
/** @type {import('./$types').PageLoad} */export async functionload ({fetch ,params }) {constres = awaitfetch (`/api/items/${params .id }`);constitem = awaitres .json ();return {item };}
ts
import type {PageLoad } from './$types';export constload :PageLoad = async ({fetch ,params }) => {constres = awaitfetch (`/api/items/${params .id }`);constitem = awaitres .json ();return {item };};
Cookiepermalink
服务器 load
函数可以获取和设置 cookies
。
ts
import * asdb from '$lib/server/database';/** @type {import('./$types').LayoutServerLoad} */export async functionload ({cookies }) {constsessionid =cookies .get ('sessionid');return {user : awaitdb .getUser (sessionid )};}
ts
import * asdb from '$lib/server/database';import type {LayoutServerLoad } from './$types';export constload :LayoutServerLoad = async ({cookies }) => {constsessionid =cookies .get ('sessionid');return {user : awaitdb .getUser (sessionid ),};};
只有当目标主机与 SvelteKit 应用程序或其更具体的子域相同,Cookie 才将通过提供的 fetch
函数传递。
例如,如果 SvelteKit 正在提供 my.domain.com
- domain.com 将不会收到 Cookie
- my.domain.com 将收到 Cookie
- api.domain.com 将不会收到 Cookie
- sub.my.domain.com 将收到 Cookie
当设置 credentials: 'include'
时,不会传递其他 Cookie,因为 SvelteKit 不知道哪个 Cookie 属于哪个域(浏览器不会传递此信息),因此转发任何 Cookie 都不安全。使用 handleFetch 挂钩 来解决此问题。
标头permalink
服务器和通用 load
函数都可以访问 setHeaders
函数,该函数在服务器上运行时可以为响应设置标头。(在浏览器中运行时,setHeaders
不起作用。)例如,如果你希望页面被缓存,这将很有用
ts
/** @type {import('./$types').PageLoad} */export async functionload ({fetch ,setHeaders }) {consturl = `https://cms.example.com/products.json`;constresponse = awaitfetch (url );// cache the page for the same length of time// as the underlying datasetHeaders ({age :response .headers .get ('age'),'cache-control':response .headers .get ('cache-control')});returnresponse .json ();}
ts
import type {PageLoad } from './$types';export constload :PageLoad = async ({fetch ,setHeaders }) => {consturl = `https://cms.example.com/products.json`;constresponse = awaitfetch (url );// cache the page for the same length of time// as the underlying datasetHeaders ({age :response .headers .get ('age'),'cache-control':response .headers .get ('cache-control'),});returnresponse .json ();};
多次设置相同的标头(即使在单独的 load
函数中)是一个错误——你只能设置给定的标头一次。你不能使用 setHeaders
添加 set-cookie
标头——请改用 cookies.set(name, value, options)
。
使用父级数据永久链接
有时,load
函数访问父级 load
函数中的数据很有用,这可以通过 await parent()
来实现
ts
/** @type {import('./$types').LayoutLoad} */export functionload () {return {a : 1 };}
ts
import type {LayoutLoad } from './$types';export constload :LayoutLoad = () => {return {a : 1 };};
ts
/** @type {import('./$types').LayoutLoad} */export async functionload ({parent }) {const {a } = awaitparent ();return {b :a + 1 };}
ts
import type {LayoutLoad } from './$types';export constload :LayoutLoad = async ({parent }) => {const {a } = awaitparent ();return {b :a + 1 };};
ts
/** @type {import('./$types').PageLoad} */export async functionload ({parent }) {const {a ,b } = awaitparent ();return {c :a +b };}
ts
import type {PageLoad } from './$types';export constload :PageLoad = async ({parent }) => {const {a ,b } = awaitparent ();return {c :a +b };};
<script>
/** @type {import('./$types').PageData} */
export let data;
</script>
<!-- renders `1 + 2 = 3` -->
<p>{data.a} + {data.b} = {data.c}</p>
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<!-- renders `1 + 2 = 3` -->
<p>{data.a} + {data.b} = {data.c}</p>
请注意,
+page.js
中的load
函数接收两个布局load
函数合并的数据,而不仅仅是直接父级。
在 +page.server.js
和 +layout.server.js
内部,parent
返回父级 +layout.server.js
文件中的数据。
在 +page.js
或 +layout.js
中,它将返回父级 +layout.js
文件中的数据。但是,缺少的 +layout.js
被视为 ({ data }) => data
函数,这意味着它还将返回父级 +layout.server.js
文件中的数据,这些文件没有被 +layout.js
文件“隐藏”
使用 await parent()
时,注意不要引入瀑布。例如,这里 getData(params)
不依赖于调用 parent()
的结果,所以我们应该首先调用它以避免延迟渲染。
/** @type {import('./$types').PageLoad} */
export async function load({ params, parent }) {
const parentData = await parent();
const data = await getData(params);
const parentData = await parent();
return {
...data
meta: { ...parentData.meta, ...data.meta }
};
}
错误永久链接
如果在 load
期间抛出错误,则会渲染最近的 +error.svelte
。对于预期错误,请使用 @sveltejs/kit
中的 error
帮助程序来指定 HTTP 状态代码和可选消息
ts
import {error } from '@sveltejs/kit';/** @type {import('./$types').LayoutServerLoad} */export functionload ({locals }) {if (!locals .user ) {error (401, 'not logged in');}if (!locals .user .isAdmin ) {error (403, 'not an admin');}}
ts
import {error } from '@sveltejs/kit';import type {LayoutServerLoad } from './$types';export constload :LayoutServerLoad = ({locals }) => {if (!locals .user ) {error (401, 'not logged in');}if (!locals .user .isAdmin ) {error (403, 'not an admin');}};
调用 error(...)
将抛出一个异常,从而可以轻松地从帮助程序函数内部停止执行。
如果抛出意外错误,SvelteKit 将调用 handleError
并将其视为 500 内部错误。
在 SvelteKit 1.x 中,你必须自己
throw
错误
重定向永久链接
要重定向用户,请使用 @sveltejs/kit
中的 redirect
帮助器来指定应将其重定向到的位置以及 3xx
状态代码。与 error(...)
一样,调用 redirect(...)
将抛出异常,从而可以轻松地从帮助器函数内部停止执行。
ts
import {redirect } from '@sveltejs/kit';/** @type {import('./$types').LayoutServerLoad} */export functionload ({locals }) {if (!locals .user ) {redirect (307, '/login');}}
ts
import {redirect } from '@sveltejs/kit';import type {LayoutServerLoad } from './$types';export constload :LayoutServerLoad = ({locals }) => {if (!locals .user ) {redirect (307, '/login');}};
不要在
try {...}
块内使用redirect()
,因为重定向将立即触发 catch 语句。
在浏览器中,你还可以使用 $app.navigation
中的 goto
在 load
函数之外以编程方式导航。
在 SvelteKit 1.x 中,你必须自己
throw
redirect
使用 Promise 进行流式传输永久链接
使用服务器 load
时,Promise 将在解析时流式传输到浏览器。如果你有缓慢的非必要数据,这很有用,因为你可以在所有数据可用之前开始渲染页面
ts
/** @type {import('./$types').PageServerLoad} */export async functionload ({params }) {return {// make sure the `await` happens at the end, otherwise we// can't start loading comments until we've loaded the postcomments :loadComments (params .slug ),post : awaitloadPost (params .slug )};}
ts
import type {PageServerLoad } from './$types';export constload :PageServerLoad = async ({params }) => {return {// make sure the `await` happens at the end, otherwise we// can't start loading comments until we've loaded the postcomments :loadComments (params .slug ),post : awaitloadPost (params .slug ),};};
例如,这对于创建骨架加载状态很有用
<script>
/** @type {import('./$types').PageData} */
export let data;
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
{#await data.comments}
Loading comments...
{:then comments}
{#each comments as comment}
<p>{comment.content}</p>
{/each}
{:catch error}
<p>error loading comments: {error.message}</p>
{/await}
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</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 以将其标记为已处理就足够了。
ts
/** @type {import('./$types').PageServerLoad} */export functionload ({fetch }) {constok_manual =Promise .reject ();ok_manual .catch (() => {});return {ok_manual ,ok_fetch :fetch ('/fetch/that/could/fail'),dangerous_unhandled :Promise .reject ()};}
ts
import type {PageServerLoad } from './$types';export constload :PageServerLoad = ({fetch }) => {constok_manual =Promise .reject ();ok_manual .catch (() => {});return {ok_manual ,ok_fetch :fetch ('/fetch/that/could/fail'),dangerous_unhandled :Promise .reject (),};};
在不支持流式传输的平台(例如 AWS Lambda 或 Firebase)上,响应将被缓冲。这意味着页面只有在所有 Promise 解析后才会渲染。如果你正在使用代理(例如 NGINX),请确保它不会缓冲来自代理服务器的响应。
只有在启用 JavaScript 时,流式传输数据才有效。如果页面是服务器渲染的,你应该避免从通用
load
函数返回 Promise,因为它们不会被流式传输——相反,当函数在浏览器中重新运行时,Promise 会被重新创建。
响应的标头和状态代码在响应开始流式传输后无法更改,因此你不能在流式传输的 Promise 中
setHeaders
或抛出重定向。
在 SvelteKit 1.x 中,顶级 Promise 会自动等待,只有嵌套 Promise 会流式传输。
并行加载永久链接
在渲染(或导航到)页面时,SvelteKit 会并发运行所有load
函数,避免请求瀑布。在客户端导航期间,调用多个服务器load
函数的结果将分组为单个响应。一旦所有load
函数返回,页面就会被渲染。
重新运行 load 函数永久链接
SvelteKit 会跟踪每个load
函数的依赖项,以避免在导航期间不必要地重新运行它。
例如,给定一对这样的load
函数...
ts
import * asdb from '$lib/server/database';/** @type {import('./$types').PageServerLoad} */export async functionload ({params }) {return {post : awaitdb .getPost (params .slug )};}
ts
import * asdb from '$lib/server/database';import type {PageServerLoad } from './$types';export constload :PageServerLoad = async ({params }) => {return {post : awaitdb .getPost (params .slug ),};};
ts
import * asdb from '$lib/server/database';/** @type {import('./$types').LayoutServerLoad} */export async functionload () {return {posts : awaitdb .getPostSummaries ()};}
ts
import * asdb from '$lib/server/database';import type {LayoutServerLoad } from './$types';export constload :LayoutServerLoad = async () => {return {posts : awaitdb .getPostSummaries (),};};
...如果我们从/blog/trying-the-raw-meat-diet
导航到/blog/i-regret-my-choices
,则+page.server.js
中的函数将重新运行,因为params.slug
已更改。+layout.server.js
中的函数不会重新运行,因为数据仍然有效。换句话说,我们不会第二次调用db.getPostSummaries()
。
如果父load
函数重新运行,则调用await parent()
的load
函数也将重新运行。
依赖项跟踪不适用于load
函数返回之后——例如,在嵌套promise中访问params.x
不会导致函数在params.x
更改时重新运行。(不用担心,如果你不小心这样做了,你将在开发中收到警告。)相反,在load
函数的主体中访问参数。
搜索参数独立于 URL 的其余部分进行跟踪。例如,在load
函数中访问event.url.searchParams.get("x")
将使该load
函数在从?x=1
导航到?x=2
时重新运行,但不会在从?x=1&y=1
导航到?x=1&y=2
时重新运行。
取消跟踪依赖项永久链接
在极少数情况下,你可能希望从依赖项跟踪机制中排除某些内容。你可以使用提供的untrack
函数来实现这一点
ts
/** @type {import('./$types').PageLoad} */export async functionload ({untrack ,url }) {// Untrack url.pathname so that path changes don't trigger a rerunif (untrack (() =>url .pathname === '/')) {return {message : 'Welcome!' };}}
ts
import type {PageLoad } from './$types';export constload :PageLoad = async ({untrack ,url }) => {// Untrack url.pathname so that path changes don't trigger a rerunif (untrack (() =>url .pathname === '/')) {return {message : 'Welcome!' };}};
手动失效永久链接
您还可以使用 invalidate(url)
重新运行适用于当前页面的 load
函数,它会重新运行所有依赖于 url
的 load
函数,以及 invalidateAll()
,它会重新运行每个 load
函数。服务器加载函数永远不会自动依赖于已获取的 url
,以避免将机密泄露给客户端。
如果 load
函数调用 fetch(url)
或 depends(url)
,则它依赖于 url
。请注意,url
可以是自定义标识符,它以 [a-z]:
开头:
ts
/** @type {import('./$types').PageLoad} */export async functionload ({fetch ,depends }) {// load reruns when `invalidate('https://api.example.com/random-number')` is called...constresponse = awaitfetch ('https://api.example.com/random-number');// ...or when `invalidate('app:random')` is calleddepends ('app:random');return {number : awaitresponse .json ()};}
ts
import type {PageLoad } from './$types';export constload :PageLoad = async ({fetch ,depends }) => {// load reruns when `invalidate('https://api.example.com/random-number')` is called...constresponse = awaitfetch ('https://api.example.com/random-number');// ...or when `invalidate('app:random')` is calleddepends ('app:random');return {number : awaitresponse .json (),};};
<script>
import { invalidate, invalidateAll } from '$app/navigation';
/** @type {import('./$types').PageData} */
export let data;
function rerunLoadFunction() {
// any of these will cause the `load` function to rerun
invalidate('app:random');
invalidate('https://api.example.com/random-number');
invalidate(url => url.href.includes('random-number'));
invalidateAll();
}
</script>
<p>random number: {data.number}</p>
<button on:click={rerunLoadFunction}>Update random number</button>
<script lang="ts">
import { invalidate, invalidateAll } from '$app/navigation';
import type { PageData } from './$types';
export let data: PageData;
function rerunLoadFunction() {
// any of these will cause the `load` function to rerun
invalidate('app:random');
invalidate('https://api.example.com/random-number');
invalidate((url) => url.href.includes('random-number'));
invalidateAll();
}
</script>
<p>random number: {data.number}</p>
<button on:click={rerunLoadFunction}>Update random number</button>
加载函数何时重新运行?永久链接
总之,load
函数将在以下情况下重新运行
- 它引用了
params
的属性,其值已更改 - 它引用了
url
的属性(例如url.pathname
或url.search
),其值已更改。request.url
中的属性不会被跟踪 - 它调用
url.searchParams.get(...)
、url.searchParams.getAll(...)
或url.searchParams.has(...)
,并且相关参数发生更改。访问url.searchParams
的其他属性将与访问url.search
产生相同的效果。 - 它调用
await parent()
,并且父load
函数重新运行 - 它通过
fetch
(仅限通用加载)或depends
声明了对特定 URL 的依赖,并且该 URL 已使用invalidate(url)
标记为无效 - 所有活动
load
函数都已使用invalidateAll()
强制重新运行
params
和 url
可以响应 <a href="..">
链接点击、<form>
交互、goto
调用或 redirect
而更改。
请注意,重新运行 load
函数将更新相应 +layout.svelte
或 +page.svelte
中的 data
属性;它不会导致重新创建组件。因此,内部状态得以保留。如果您不希望这样,您可以在 afterNavigate
回调中重置您需要重置的所有内容,和/或将您的组件包装在 {#key ...}
块中。
对身份验证的影响永久链接
加载数据的一些特性对身份验证检查有重要影响
- 布局
load
函数不会在每个请求上运行,例如在子路由之间的客户端导航期间。 (加载函数何时重新运行?) - 布局和页面
load
函数并发运行,除非调用了await parent()
。如果布局load
抛出异常,则页面load
函数会运行,但客户端不会收到返回的数据。
有几种可能的策略可以确保在受保护代码之前进行身份验证检查。
防止数据瀑布并保留布局load
缓存
- 在任何
load
函数运行之前,使用hooks保护多个路由 - 在
+page.server.js
load
函数中直接使用身份验证保护,以实现特定路由保护
在+layout.server.js
中放置身份验证保护需要所有子页面在受保护代码之前调用await parent()
。除非每个子页面都依赖于await parent()
返回的数据,否则其他选项将具有更好的性能。