mcpField
S
sveltekit-best-practices

sveltekit-best-practices

github.com/ofershap开发1379 次安装36 周前更新
代码仓库

关于 sveltekit-best-practices

sveltekit-best-practices 是面向使用 SvelteKit 或 Svelte 5 的编码 Agent 的开发技能。它强调 Svelte 5 响应式语法及 SvelteKit 的数据加载、表单操作和路由约定,以避免沿用 Svelte 4 模式。

主要功能

  1. 01使用 Svelte 5 runes 管理状态
  2. 02通过 $props() 声明组件属性
  3. 03使用 load 函数获取页面数据
  4. 04通过表单 actions 处理提交
  5. 05使用布局加载共享数据
  6. 06遵循 SvelteKit 错误页与 hooks 约定

适用场景

  1. 01编写 Svelte 5 组件与响应式逻辑
  2. 02为页面实现服务端数据加载
  3. 03构建渐进增强的表单提交
  4. 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

评论

支持 Markdown