关于 sveltekit-best-practices
sveltekit-best-practices 是面向使用 SvelteKit 或 Svelte 5 的编码 Agent 的开发技能。它强调 Svelte 5 响应式语法及 SvelteKit 的数据加载、表单操作和路由约定,以避免沿用 Svelte 4 模式。
主要功能
- 01使用 Svelte 5 runes 管理状态
- 02通过 $props() 声明组件属性
- 03使用 load 函数获取页面数据
- 04通过表单 actions 处理提交
- 05使用布局加载共享数据
- 06遵循 SvelteKit 错误页与 hooks 约定
适用场景
- 01编写 Svelte 5 组件与响应式逻辑
- 02为页面实现服务端数据加载
- 03构建渐进增强的表单提交
- 04配置共享布局数据与请求中间件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ ofershap/ sveltekit-best-practices --skill sveltekit-best-practices安装后在 Claude Code 中输入/sveltekit-best-practices确认已加载。
详细介绍
使用时机
在处理 SvelteKit 或 Svelte 5 代码时使用此技能。AI Agent 的训练数据中包含 Svelte 4 模式,因此经常会使用 stores、响应式声明和 export let 生成过时的代码。 此技能要求使用 Svelte 5 runes、load 函数和表单 actions。
关键规则
1. 使用 Svelte 5 runes——不要使用 Svelte 4 stores 或响应式声明
错误(Agent 常见写法):
<script>
import { writable, derived } from 'svelte/store';
let count = writable(0);
$: doubled = $count * 2;
$: if (count > 5) alert('too high');
</script>
<p>{$count}</p>
正确:
<script>
let count = $state(0);
let doubled = $derived(count * 2);
$effect(() => {
if (count > 5) alert('too high');
});
</script>
<p>{count}</p>
原因: Svelte 5 runes($state、$derived、$effect)取代了 stores 和 $: 语法。Agent 默认会采用 Svelte 4 模式。
2. 使用 $state 管理响应式状态——不要使用带响应式赋值的 let
错误:
<script>
let count = 0;
count = count + 1;
</script>
正确:
<script>
let count = $state(0);
count = count + 1;
</script>
原因: 在 Svelte 5 中,响应性通过 $state 显式启用。普通 let 不具备响应性。
3. 使用 $derived 计算派生值——不要使用 $: 响应式声明
错误:
<script>
let firstName = $state('John');
let lastName = $state('Doe');
$: fullName = `${firstName} ${lastName}`;
</script>
正确:
<script>
let firstName = $state('John');
let lastName = $state('Doe');
let fullName = $derived(`${firstName} ${lastName}`);
</script>
原因: $: 属于 Svelte 4。Svelte 5 使用 $derived 来进行派生计算。
4. 使用 $effect 处理副作用——不要使用 $: 响应式语句
错误:
<script>
let count = $state(0);
$: if (count > 5) console.log('count is high');
</script>
正确:
<script>
let count = $state(0);
$effect(() => {
if (count > 5) console.log('count is high');
});
</script>
原因: 依赖项发生变化时,$effect 会运行。使用 $: 处理副作用的做法已弃用。
5. 使用 $props() 获取组件属性——不要使用 export let
错误:
<script>
export let title = 'Default';
export let count;
</script>
<h1>{title}</h1>
正确:
<script>
let { title = 'Default', count } = $props();
</script>
<h1>{title}</h1>
原因: export let 属于 Svelte 4。Svelte 5 使用 $props()。
6. 使用 $bindable() 实现双向绑定属性
错误:
<script>
let { value } = $props();
</script>
<input bind:value={value} />
正确:
<script>
let { value = $bindable() } = $props();
</script>
<input bind:value={value} />
原因: 属性默认是单向的。$bindable() 可启用来自父组件的 bind:value。
7. 使用 load 函数(+page.server.ts)获取数据——不要使用 onMount fetch
错误:
<script>
import { onMount } from 'svelte';
let data = $state(null);
onMount(async () => {
data = await fetch('/api/users').then(r => r.json());
});
</script>
{#if data}{data.name}{/if}
正确:
// +page.server.ts
import type { PageServerLoad } from "./$types";
export const load: PageServerLoad = async ({ fetch }) => ({
data: await fetch("/api/users").then((r) => r.json()),
});
<script>
let { data } = $props();
</script>
{#if data}{data.name}{/if}
原因: load 会在服务器上运行以支持 SSR,避免加载闪烁,并与 SvelteKit 路由集成。
8. 使用表单 actions 处理变更——不要为表单提交使用 API 路由
错误:
<form on:submit={async (e) => {
e.preventDefault();
await fetch('/api/login', { method: 'POST', body: new FormData(e.target) });
goto('/dashboard');
}}>
正确:
// +page.server.ts
import type { Actions } from "./$types";
export const actions = {
default: async ({ request, cookies }) => {
const data = await request.formData();
// validate, authenticate, set cookie
return { type: "redirect", location: "/dashboard" };
},
};
<form method="POST" use:enhance>
原因: 表单 actions 支持渐进增强,无需 JS 也能运行,并可避免客户端 fetch 样板代码。
9. 使用 +layout.server.ts 提供共享布局数据
错误:
<script>
let user = $state(null);
onMount(() => fetchUser().then(u => user = u));
</script>
正确:
// +layout.server.ts
export const load = async ({ locals }) => ({
user: locals.user,
});
原因: 布局 load 只运行一次,所有子页面都能访问数据,不会重复 fetch。
10. 使用 +error.svelte 创建错误页面
正确:
<script>
let { status, message } = $props();
</script>
<h1>{status}</h1>
<p>{message}</p>
原因: SvelteKit 使用 +error.svelte 渲染 load/action 错误。请使用它,而不是在 每个页面中都使用 try/catch。
11. 使用 +page.ts 执行通用 load(服务器端和客户端)
当服务器端和客户端都需要数据时(例如来自 $app/stores 或浏览器 API),请将逻辑放在 +page.ts 中。仅服务器端需要数据时使用 +page.server.ts。
12. 使用 hooks.server.ts 实现中间件(身份验证、重定向)
正确:
// hooks.server.ts
export const handle = async ({ event, resolve }) => {
event.locals.user = await getUser(event);
if (!event.locals.user && event.url.pathname.startsWith("/dashboard")) {
return redirect(302, "/login");
}
return resolve(event);
};
原因: Handle 会在每次请求前运行。可用于身份验证、重定向和设置 locals。
13. 谨慎使用 $app/stores——优先使用 load 函数数据
优先通过 load 属性传递数据。仅在需要客户端路由状态时使用 $page、$navigating 等。
14. 使用 snippet 块复用模板片段(Svelte 5)
错误:
<script>
export let slots;
</script>
{#if slots.header}<slot name="header" />{/if}
正确:
<script>
let { header = @render(() => {}) } = $props();
</script>
{@render header()}
原因: Svelte 5 snippets 使用 @render 和 snippet 属性,取代了基于 slot 的组合方式。
模式
- 页面数据:+page.server.ts load 返回对象,+page.svelte 通过 $props() 接收
data - 带 enhance 的表单:使用
method="POST"和来自$app/forms的use:enhance - 流式传输:从 load 返回 promise,不要使用 await;在模板中使用
{#await data.promise} - TypeScript:使用
import type { PageServerLoad, PageProps } from './$types'
反模式
- 不要使用 svelte/store 中的 writable、readable、derived、get
- 不要使用 $: 进行派生计算或处理副作用
- 不要使用 export let 定义属性
- SSR 需要数据时,不要在 onMount 中 fetch
- 不要仅为处理表单 POST 而创建 +server.ts API 路由
- 属性不是 $bindable() 时,不要对其使用 bind:value