数据加载(load 函数)
本教程共 50 篇 · 第 46 篇 · 更新于 2026-08-05 · 约 6 分钟阅读
46. 数据加载(load 函数)
本节目标:掌握 SvelteKit 的
load函数,理解 universal load 和 server load 的区别,学会用params、fetch、parent获取数据,以及用invalidate刷新数据。
load 函数是什么
页面渲染前通常需要加载数据。SvelteKit 用 load 函数来完成这个工作——它在页面组件渲染之前执行,返回的数据通过 data prop 传给组件。
// src/routes/blog/[slug]/+page.js
/** @type {import('./$types').PageLoad} */
export function load({ params }) {
return {
post: {
title: `${params.slug} 的标题`,
content: '文章内容...'
}
};
}
<!-- src/routes/blog/[slug]/+page.svelte -->
<script>
/** @type {import('./$types').PageProps} */
let { data } = $props();
</script>
<h1>{data.post.title}</h1>
<div>{data.post.content}</div>
load 返回的对象就是 data,页面组件直接读取。
Universal vs Server load
load 函数有两种写法,放在不同文件里:
| 文件 | 类型 | 运行环境 | 返回值限制 |
|---|---|---|---|
+page.js | universal load | 服务端 + 客户端 | 任意值(含组件构造器) |
+page.server.js | server load | 仅服务端 | 必须可序列化 |
Universal load 在 SSR 时服务端运行,水合时浏览器端再运行一次(复用 fetch 缓存),后续导航只在浏览器运行。Server load 永远在服务端运行。
// src/routes/blog/[slug]/+page.server.js
import * as db from '$lib/server/database';
/** @type {import('./$types').PageServerLoad} */
export async function load({ params }) {
const post = await db.getPost(params.slug);
return { post };
}
Tip需要访问数据库或私密环境变量时用
+page.server.js。从公开 API 获取数据时用+page.js,SvelteKit 能直接从 API 获取数据而不经过你的服务器。
两者可以同时存在。Server load 的返回值会作为 universal load 的 data 参数传入:
// +page.server.js
export async function load() {
return { serverMessage: '来自服务端' };
}
// +page.js
/** @type {import('./$types').PageLoad} */
export async function load({ data }) {
return {
serverMessage: data.serverMessage,
universalMessage: '来自通用 load'
};
}
布局数据
布局也可以有 load 函数,返回的数据会传给所有子页面:
// src/routes/blog/[slug]/+layout.server.js
import * as db from '$lib/server/database';
/** @type {import('./$types').LayoutServerLoad} */
export async function load() {
return {
posts: await db.getPostSummaries()
};
}
<!-- src/routes/blog/[slug]/+page.svelte -->
<script>
/** @type {import('./$types').PageProps} */
let { data } = $props();
</script>
<!-- data 同时包含布局和页面的 load 数据 -->
<h1>{data.post.title}</h1>
<aside>
{#each data.posts as post}
<a href="/blog/{post.slug}">{post.title}</a>
{/each}
</aside>
Note如果布局和页面的
load返回同名 key,页面的值会覆盖布局的值。
访问 URL 数据
load 函数接收一个事件对象,包含 params、url 和 route:
/** @type {import('./$types').PageLoad} */
export function load({ params, url, route }) {
// params:路由参数
console.log(params.slug);
// url:URL 对象
console.log(url.pathname);
console.log(url.searchParams.get('q'));
// route:路由信息
console.log(route.id); // '/blog/[slug]'
}
url.searchParams 可以读取查询参数。注意预渲染时不能访问 searchParams。
fetch 请求
load 函数中用内置的 fetch 获取外部数据。它和原生 fetch 一样,但有几个增强:
// src/routes/items/[id]/+page.js
/** @type {import('./$types').PageLoad} */
export async function load({ fetch, params }) {
const res = await fetch(`/api/items/${params.id}`);
const item = await res.json();
return { item };
}
增强点:
- SSR 时继承请求的 cookie 和 authorization 头
- 服务端可以发相对路径请求
- 内部请求(如
+server.js)在服务端直接调用处理函数,不走 HTTP - SSR 时的响应会被内联到 HTML 中,水合时直接读取,不重复请求
Note不要用原生
fetch,要用load提供的fetch。否则水合时会重复请求,浏览器控制台会有警告。
Cookies
Server load 可以读取和设置 cookies:
// src/routes/+layout.server.js
import * as db from '$lib/server/database';
/** @type {import('./$types').LayoutServerLoad} */
export async function load({ cookies }) {
const sessionid = cookies.get('sessionid');
return {
user: await db.getUser(sessionid)
};
}
数据依赖:parent
如果页面 load 需要布局 load 的数据,用 parent:
// src/routes/blog/[slug]/+page.server.js
/** @type {import('./$types').PageServerLoad} */
export async function load({ parent, params }) {
const { posts } = await parent(); // 获取布局的 load 数据
const post = posts.find(p => p.slug === params.slug);
return { post };
}
Tip谨慎使用
parent。它会让页面load等布局load完成后才开始,可能影响性能。如果数据不依赖父布局,不要调用parent。
数据刷新:invalidate
有时候数据需要刷新。用 invalidate 让 load 函数重新运行:
<script>
import { invalidate } from '$app/navigation';
async function refresh() {
// 让依赖 'custom:data' 的 load 重新运行
await invalidate('custom:data');
}
</script>
<button onclick={refresh}>刷新数据</button>
在 load 中用 depends 声明依赖:
/** @type {import('./$types').PageLoad} */
export function load({ depends }) {
depends('custom:data');
return {
time: new Date().toLocaleTimeString()
};
}
invalidateAll 让所有 load 重新运行:
import { invalidateAll } from '$app/navigation';
await invalidateAll();
流式数据
Server load 可以返回 Promise,实现数据流式传输:
// src/routes/+page.server.js
/** @type {import('./$types').PageServerLoad} */
export function load() {
return {
// 快速返回的数据
title: '页面标题',
// 慢速数据用 Promise,不阻塞首屏
stats: new Promise((resolve) => {
setTimeout(() => resolve({ visits: 42 }), 3000);
})
};
}
<script>
/** @type {import('./$types').PageProps} */
let { data } = $props();
</script>
<h1>{data.title}</h1>
{#await data.stats}
<p>加载中...</p>
{:then stats}
<p>访问量:{stats.visits}</p>
{/await}
Note流式数据需要部署平台支持流式响应。部分平台(如 AWS Lambda)会缓冲响应,流式效果会失效。
page.data
父布局可以访问子页面的数据。用 $app/state 的 page 对象:
<!-- src/routes/+layout.svelte -->
<script>
import { page } from '$app/state';
</script>
<svelte:head>
<title>{page.data.title}</title>
</svelte:head>
page.data 包含从根布局到当前页面的所有 load 数据的合并结果。
本节回顾
+page.js导出 universal load(服务端 + 客户端),+page.server.js导出 server load(仅服务端)- Server load 必须返回可序列化的数据,universal load 可以返回任意值
- 布局的
load数据会传给所有子页面 - 用
params、url、route访问 URL 信息 - 用内置
fetch请求数据,SSR 时会内联响应避免重复请求 parent()获取父布局的 load 数据invalidate()刷新特定依赖的 load,invalidateAll()刷新所有- Server load 返回 Promise 实现流式数据传输