mcpField
S
svelte5-development

svelte5-development

github.com/autumnsgrove开发779 次安装2 周前更新
代码仓库

关于 svelte5-development

Svelte 5 Development Skill 为 Svelte 5 和 SvelteKit 项目提供开发指导,涵盖 runes、路由、数据加载、表单 actions、API 路由及常见实现问题。

主要功能

  1. 01Svelte 5 rune 参考
  2. 02SvelteKit 文件约定
  3. 03数据加载与 API 路由
  4. 04表单 actions 与验证
  5. 05常见问题和模式

适用场景

  1. 01使用 runes 创建 Svelte 5 组件
  2. 02设置 SvelteKit 路由和布局
  3. 03为页面加载数据
  4. 04实现带验证的表单 actions

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/autumnsgrove/lattice --skill svelte5-development

安装后在 Claude Code 中输入/svelte5-development确认已加载。

详细介绍

Svelte 5 开发技能

激活时机

在以下情况下激活此技能:

  • 创建 Svelte 5 组件
  • 使用 SvelteKit 路由
  • 实现 Runes($state、$derived、$effect)
  • 使用 actions 构建表单
  • 搭建 SvelteKit 项目

快速命令

npx sv create my-app      # Create SvelteKit project
cd my-app && pnpm install
pnpm dev               # Start dev server (localhost:5173)
pnpm build             # Build for production

Runes 快速参考

Rune用途示例
$state响应式状态let count = $state(0)
$derived计算值let doubled = $derived(count * 2)
$effect副作用$effect(() => console.log(count))
$props组件属性let { name } = $props()
$bindable双向绑定let { value = $bindable() } = $props()

响应式状态($state)

<script>
	let count = $state(0);
	let user = $state({ name: "Alice", age: 30 });
	let items = $state(["apple", "banana"]);
</script>

<button onclick={() => count++}>
	Clicked {count} times
</button>

<button onclick={() => user.age++}>
	{user.name} is {user.age}
</button>

深层响应性:对象和数组在发生变更时会自动更新。

计算值($derived)

<script>
	let count = $state(0);
	let doubled = $derived(count * 2);

	// For complex computations
	let summary = $derived.by(() => {
		const total = items.length;
		const done = items.filter((i) => i.done).length;
		return `${done}/${total}`;
	});
</script>

副作用($effect)

<script>
	let count = $state(0);

	$effect(() => {
		console.log(`Count is ${count}`);
		document.title = `Count: ${count}`;

		// Cleanup function
		return () => {
			console.log("Cleanup");
		};
	});
</script>

组件属性($props)


<script>
	let { label, disabled = false, onclick } = $props();
</script>

<button {disabled} {onclick}>{label}</button>
TypeScript 属性
<script lang="ts">
	interface Props {
		label: string;
		disabled?: boolean;
		onclick?: () => void;
	}

	let { label, disabled = false, onclick }: Props = $props();
</script>

SvelteKit 文件约定

文件用途
+page.svelte页面组件
+page.server.js仅限服务端的加载/操作
+layout.svelte共享布局
+server.jsAPI 端点
+error.svelte错误边界

数据加载

// src/routes/posts/+page.server.js
export async function load({ fetch }) {
	const response = await fetch("/api/posts");
	return { posts: await response.json() };
}

<script>
	let { data } = $props();
</script>

{#each data.posts as post}
	<article>
		<h2>{post.title}</h2>
	</article>
{/each}

表单操作

类型安全的表单验证(Rootwork 模式)

使用 parseFormData() 在信任边界处安全地处理表单类型:

// src/routes/login/+page.server.ts
import { fail, redirect } from "@sveltejs/kit";
import { parseFormData } from "@autumnsgrove/lattice/server";
import { z } from "zod";

const LoginSchema = z.object({
	email: z.string().email("Valid email required"),
	password: z.string().min(8, "Password must be at least 8 characters"),
});

export const actions = {
	default: async ({ request, cookies }) => {
		const formData = await request.formData();
		const result = parseFormData(formData, LoginSchema);

		if (!result.success) {
			const firstError = Object.values(result.errors).flat()[0];
			return fail(400, { error: firstError || "Invalid input" });
		}

		const { email, password } = result.data;

		// Authenticate with validated, typed data
		const token = await authenticate(email, password);
		if (!token) {
			return fail(401, { error: "Invalid credentials" });
		}

		cookies.set("session", token, { path: "/" });
		throw redirect(303, "/dashboard");
	},
};

优点:

  • 类型安全:result.data 完全符合 LoginSchema 类型
  • 结构化错误:result.errors 是字段 → 消息的映射
  • 信任边界处无需使用 as 类型断言

<script>
	import { enhance } from "$app/forms";
	let { form } = $props();
</script>

<form method="POST" use:enhance>
	<input name="email" value={form?.email ?? ""} />
	{#if form?.missing}
		<p class="error">Email required</p>
	{/if}
	<button>Submit</button>
</form>

API 路由

// src/routes/api/posts/+server.js
import { json } from "@sveltejs/kit";

export async function GET({ url }) {
	const posts = await getPosts();
	return json(posts);
}

export async function POST({ request }) {
	const data = await request.json();
	const post = await createPost(data);
	return json(post, { status: 201 });
}

常见问题

解构会破坏响应性
// ❌ Bad
let { count } = $state({ count: 0 });

// ✅ Good
let state = $state({ count: 0 });
state.count++;
Each 中缺少键

{#each items as item}


{#each items as item (item.id)}
使用渐进增强
<script>
  import { enhance } from '$app/forms';
</script>

<form method="POST" use:enhance>
错误反馈(Signpost + Toast)

Toast 是用户操作的主要客户端反馈方式:

import { toast } from "@autumnsgrove/lattice/ui";

// After successful action
toast.success("Post published!");

// After failed action
toast.error(err instanceof Error ? err.message : "Something went wrong");

// Async operations
toast.promise(apiRequest("/api/export", { method: "POST" }), {
	loading: "Exporting...",
	success: "Done!",
	error: "Export failed.",
});

// Multi-step flows
const id = toast.loading("Saving...");
// ... later
toast.dismiss(id);
toast.success("Saved!");

**不应使用 Toast 的情况:**表单验证(使用 fail() + 行内错误)、页面加载失败(+error.svelte)、持久通知(使用 GroveMessages)。

服务端错误使用 Signpost 代码:

import {
	API_ERRORS,
	buildErrorJson,
	throwGroveError,
	logGroveError,
} from "@autumnsgrove/lattice/errors";

// API route: return structured error
return json(buildErrorJson(API_ERRORS.RESOURCE_NOT_FOUND), { status: 404 });

// Page load: throw to +error.svelte
throwGroveError(404, SITE_ERRORS.PAGE_NOT_FOUND, "Engine");

使用 isRedirect() 和 isHttpError() 安全处理表单操作中捕获的异常:

import { isRedirect, isHttpError } from "@autumnsgrove/lattice/server";
import { fail } from "@sveltejs/kit";

export const actions = {
	default: async ({ request }) => {
		try {
			const formData = await request.formData();
			const result = parseFormData(formData, MySchema);

			if (!result.success) {
				return fail(400, { error: "Validation failed" });
			}

			// Process data
			const response = await processData(result.data);
			throw redirect(303, "/success");
		} catch (err) {
			// Redirects and HTTP errors must be re-thrown
			if (isRedirect(err)) throw err;
			if (isHttpError(err)) return fail(err.status, { error: err.body.message });

			// Unexpected errors
			console.error("Unexpected error:", err);
			return fail(500, { error: "Something went wrong" });
		}
	},
};

完整参考请参阅 AgentUsage/error_handling.md 和 AgentUsage/rootwork_type_safety.md。

项目结构

src/
├── lib/
│   ├── components/
│   └── server/
├── routes/
│   ├── +layout.svelte
│   ├── +page.svelte
│   └── api/
├── app.html
└── hooks.server.js

Grove 专属:GroveTerm 组件

构建包含自然主题术语的 Grove UI 时,请始终使用 GroveTerm 组件套件,而不要硬编码术语。这样可以遵循用户的 Grove Mode 设置(默认使用标准术语,用户可选择启用 Grove 词汇)。

<script lang="ts">
	import { GroveTerm, GroveSwap, GroveText } from "@autumnsgrove/lattice/ui";
	import groveTermManifest from "$lib/data/grove-term-manifest.json";
</script>


<GroveTerm term="bloom" manifest={groveTermManifest} />


<GroveSwap term="arbor" manifest={groveTermManifest} />


<GroveText
	content="Your [[bloom|posts]] live in your [[garden|blog]]."
	manifest={groveTermManifest}
/>

组件源码请参阅 libs/engine/src/lib/ui/components/ui/groveterm/,完整的 UI 设计指南请参阅 chameleon-adapt 技能。

相关资源

完整文档请参阅 AgentUsage/svelte5_guide.md,其中包括:

  • 高级 Rune 模式
  • 可复用标记片段
  • 性能优化
  • TypeScript 集成

评论

支持 Markdown