首页 / Svelte 5 入门教程 / 数据加载(load 函数)

Svelte 5 入门教程

数据加载(load 函数)

本教程共 50 篇 · 第 46 篇 · 更新于 2026-08-05 · 约 6 分钟阅读

SvelteSvelteKitload数据加载SSR

本节目标:掌握 SvelteKit 的 load 函数,理解 universal load 和 server load 的区别,学会用 paramsfetchparent 获取数据,以及用 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.jsuniversal load服务端 + 客户端任意值(含组件构造器)
+page.server.jsserver 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 函数接收一个事件对象,包含 paramsurlroute

/** @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

有时候数据需要刷新。用 invalidateload 函数重新运行:

<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/statepage 对象:

<!-- 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 数据会传给所有子页面
  • paramsurlroute 访问 URL 信息
  • 用内置 fetch 请求数据,SSR 时会内联响应避免重复请求
  • parent() 获取父布局的 load 数据
  • invalidate() 刷新特定依赖的 load,invalidateAll() 刷新所有
  • Server load 返回 Promise 实现流式数据传输