在 SvelteKit 中,load
事件中的 depends
函数用于声明你的 load
函数返回的数据(或其中一部分)依赖于一个特定的标识符(例如 URL 或自定义键)。
depends
的主要目的是允许你使用 $app/navigation
中的 invalidate(key)
从客户端选择性地重新运行 你的 load
函数(或使其特定部分失效)。这对于在不进行完整页面刷新的情况下更新当前页面上的数据非常有用,尤其是在用户操作(例如添加、更新或删除项目)改变了底层数据之后。
让我们通过一个例子来分解它:
场景: 我们想要显示一个"待办事项"列表。当用户通过同一页面上的表单添加新的待办事项时,我们希望待办事项列表能够自动刷新,而无需进行完整的页面导航。
示例:使用 depends
的待办事项列表
我们将创建一个简单的页面,用于显示待办事项并允许添加新的待办事项。
1. 项目设置(如果你还没有):
bash
npm create svelte@latest my-sveltekit-app
cd my-sveltekit-app
npm install
# 建议选择 TypeScript 以获得类型安全,但它也适用于 JavaScript。
2. 创建文件:
src/
├── routes/
│ ├── api/
│ │ └── todos/
│ │ └── +server.ts <-- 我们的模拟待办事项 API
│ └── todos/
│ ├── +page.svelte <-- Svelte 组件 (UI)
│ └── +page.ts <-- 带有 depends 的 load 函数
└── app.d.ts
文件 1: src/routes/api/todos/+server.ts
(模拟 API)
这将是我们的待办事项的简单内存"数据库"。
typescript
// src/routes/api/todos/+server.ts
import { json } from '@sveltejs/kit';
interface Todo {
id: number;
text: string;
completed: boolean;
}
// 模拟的待办事项数据
let todos: Todo[] = [
{ id: 1, text: '学习 SvelteKit', completed: false },
{ id: 2, text: '构建一个 SvelteKit 应用', completed: false }
];
let nextId = 3;
// GET 请求:返回所有待办事项
export async function GET() {
return json(todos);
}
// POST 请求:添加一个新的待办事项
export async function POST(event) {
const { text } = await event.request.json();
if (!text) {
return json({ error: 'Text is required' }, { status: 400 });
}
const newTodo: Todo = {
id: nextId++,
text,
completed: false
};
todos.push(newTodo);
return json(newTodo, { status: 201 });
}
文件 2: src/routes/todos/+page.ts
(带有 depends
的 load
函数)
这就是我们使用 event.depends()
的地方。
typescript
// src/routes/todos/+page.ts
import type { PageLoad } from './$types';
export const load: PageLoad = async ({ fetch, depends }) => {
// 声明对 'api:todos' 键的依赖。
// 这告诉 SvelteKit,此 load 函数返回的数据
// 依赖于与 'api:todos' 关联的状态。
depends('api:todos');
// 从我们的模拟 API 获取待办事项
const response = await fetch('/api/todos');
const todos = await response.json();
return {
todos
};
};
文件 3: src/routes/todos/+page.svelte
(UI 组件)
此组件将显示待办事项并提供一个表单来添加新的待办事项。关键是,在添加待办事项后,它将调用 invalidate('api:todos')
。
svelte
<!-- src/routes/todos/+page.svelte -->
<script lang="ts">
import type { PageData } from './$types';
import { invalidate } from '$app/navigation';
export let data: PageData; // 来自 +page.ts 的数据类型
let newTodoText = '';
let loading = false;
let error: string | null = null;
async function addTodo() {
loading = true;
error = null;
try {
const response = await fetch('/api/todos', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ text: newTodoText })
});
if (!response.ok) {
const resData = await response.json();
throw new Error(resData.error || 'Failed to add todo');
}
// !!! 奇迹发生在这里 !!!
// 成功添加待办事项后,我们使 'api:todos' 依赖项失效。
// 这告诉 SvelteKit 重新运行任何声明了对 'api:todos' 依赖关系的
// load 函数。
// 在我们的例子中,它将重新运行 `src/routes/todos/+page.ts`,重新获取列表。
await invalidate('api:todos');
newTodoText = ''; // 清空输入框
} catch (e: any) {
error = e.message;
} finally {
loading = false;
}
}
</script>
<svelte:head>
<title>SvelteKit 待办事项</title>
</svelte:head>
<h1>我的待办事项</h1>
{#if data.todos.length === 0}
<p>还没有待办事项!在下面添加一个。</p>
{:else}
<ul>
{#each data.todos as todo (todo.id)}
<li>{todo.text} {todo.completed ? '(已完成)' : ''}</li>
{/each}
</ul>
{/if}
<form on:submit|preventDefault={addTodo}>
<input type="text" bind:value={newTodoText} placeholder="添加一个新的待办事项..." required />
<button type="submit" disabled={loading}>
{loading ? '正在添加...' : '添加待办事项'}
</button>
</form>
{#if error}
<p style="color: red;">错误: {error}</p>
{/if}
<style>
ul {
list-style: none;
padding: 0;
}
li {
padding: 8px 0;
border-bottom: 1px solid #eee;
}
li:last-child {
border-bottom: none;
}
form {
margin-top: 20px;
}
input {
padding: 8px;
margin-right: 10px;
width: 250px;
}
button {
padding: 8px 15px;
cursor: pointer;
}
</style>
工作原理:
-
+page.ts
(Load 函数):depends('api:todos')
:这一行是关键。它声明此load
函数返回的数据依赖于标识符'api:todos'
。此键可以是任何字符串,但最佳实践是使其具有描述性(例如,scheme:path
)。- 然后它从
/api/todos
fetch
(获取)当前的待办事项列表。
-
+page.svelte
(UI 组件):- 显示最初加载的
data.todos
。 - 当"添加待办事项"表单提交时,调用
addTodo()
。 fetch('/api/todos', { method: 'POST', ... })
将新的待办事项发送到我们的模拟 API。await invalidate('api:todos');
:这是最关键的部分。 在新的待办事项成功添加到服务器后,我们调用invalidate('api:todos')
。- SvelteKit 会检测到这个
invalidate
调用。 - 然后它会检查所有声明了对
'api:todos'
依赖关系的活动load
函数。 - 在我们的例子中,它找到了
src/routes/todos/+page.ts
。 - 然后它无需进行完整的页面导航 就重新运行该
load
函数。 +page.ts
重新获取更新后的待办事项列表(现在包含了新的待办事项)。- SvelteKit 无缝地更新
+page.svelte
中的data
属性,并且 UI 会响应式地重新渲染以显示新的待办事项。
- SvelteKit 会检测到这个
- 显示最初加载的
关键要点:
- 在
load
函数中使用depends(key)
: 声明一个依赖关系。 - 从
$app/navigation
中使用invalidate(key)
: 触发声明了对该特定key
依赖的load
函数(或相关部分)的重新运行。 key
格式: 使用描述性键,通常是scheme:path
(例如api:users
、data:products:123
、todos:active
)。invalidate
的使用时机: 通常在数据修改操作(例如POST
、PUT
、DELETE
)之后,这些操作可能来自表单提交、客户端发起的 API 调用或 SvelteKit 表单action
内部。- 好处: 无需完整页面刷新即可实现高度动态和响应式的用户界面,显著提升用户体验。
invalidateAll()
: SvelteKit 还提供了invalidateAll()
,它会重新运行当前页面上所有 活动的load
函数,无论它们是否有依赖关系。请使用invalidate(key)
进行更精细的控制。