用Claude Code高效开发Svelte与SvelteKit
面向初学者的Claude Code与Svelte/SvelteKit实战指南,覆盖runes、路由、表单actions、测试和安全改动方式。
为什么Claude Code适合SvelteKit开发
Svelte是一个把组件编译成轻量JavaScript的UI框架。SvelteKit在它之上提供文件路由、服务端渲染、load函数、表单actions、API端点和部署适配器。对于初学者来说,Svelte的语法很直接,但一个真实功能往往会同时改到 src/routes、src/lib/components、$lib/server、类型和测试,这正是Claude Code能发挥作用的地方。
Claude Code是Anthropic官方的agentic coding工具,可以读取代码库、编辑文件、运行命令,并在终端、IDE、桌面和浏览器环境中工作。使用它开发SvelteKit时,关键不是让它“一次生成完整应用”,而是让它先读相关文件,再按小范围修改。这样既能利用AI速度,也能保留人类对路由、权限、数据边界和发布风险的判断。
本文用一个任务管理小应用做例子,讲清楚项目创建、Svelte 5 runes、共享状态、SvelteKit路由、form actions、测试、Claude Code提示词、实际用例和常见坑。runes可以理解为“响应式语法标记”:$props接收父组件输入,$state声明会变化的状态,$derived从状态计算显示值,$effect处理浏览器中的副作用。
flowchart LR
A["写清小需求"] --> B["让Claude Code读取相关文件"]
B --> C["修改Svelte组件"]
C --> D["检查load与actions"]
D --> E["运行类型检查和测试"]
E --> F["人工审查git diff"]
项目创建与Claude Code启动
新建SvelteKit项目时,当前官方推荐从Svelte CLI开始,也就是 npx sv create my-app。如果只是做一个独立的Svelte组件应用,也可以用Vite的 svelte-ts 模板。Vite官方指南现在要求Vite运行在Node.js 20.19+或22.12+,如果你的机器还停在旧版本Node,先升级再排查Svelte错误会更省时间。
npx sv create claude-svelte-demo
cd claude-svelte-demo
npm install
npm run dev
claude
第一次让Claude Code进入项目时,建议先用plan mode。这个模式会读取文件和提出计划,但不会直接改源码。
/plan
我想在这个SvelteKit项目中增加任务列表功能。
请先读取src/routes和src/lib,说明需要修改哪些文件、数据如何流动、应该补哪些测试。
现在不要编辑文件。
也可以从命令行直接启动:
claude --permission-mode plan
团队规则应写进 CLAUDE.md 或 .claude/CLAUDE.md。例如:“优先使用Svelte 5 runes”“表单提交使用SvelteKit actions”“秘密信息只能放在 $lib/server”“完成前运行 npm run check”“未经要求不提交commit”。规则越具体,Claude Code越容易遵守。
用Svelte 5写小组件
下面的 TaskCard.svelte 可以直接放到 src/lib/components/TaskCard.svelte。它接收一个任务对象和一个回调函数,用 $derived 计算显示标签。示例保留了按钮的 aria-pressed,因为可访问性不应该在AI改代码时被牺牲。
<!-- src/lib/components/TaskCard.svelte -->
<script lang="ts">
type Task = {
id: string;
title: string;
done: boolean;
estimateMinutes: number;
tags: string[];
};
let {
task,
onToggle
}: {
task: Task;
onToggle: (id: string) => void;
} = $props();
let statusLabel = $derived(task.done ? '已完成' : '未完成');
let estimateLabel = $derived(`${Math.ceil(task.estimateMinutes / 15) * 15}分钟块`);
</script>
<article class:done={task.done} class="task-card">
<div>
<p class="status">{statusLabel}</p>
<h3>{task.title}</h3>
<p>{estimateLabel}</p>
</div>
<ul aria-label="标签">
{#each task.tags as tag}
<li>{tag}</li>
{/each}
</ul>
<button type="button" aria-pressed={task.done} onclick={() => onToggle(task.id)}>
{task.done ? '标为未完成' : '标为完成'}
</button>
</article>
<style>
.task-card {
display: grid;
gap: 0.75rem;
border: 1px solid #ddd;
border-radius: 0.5rem;
padding: 1rem;
}
.done {
background: #f2fff5;
}
.status {
font-size: 0.875rem;
font-weight: 700;
}
ul {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
list-style: none;
padding: 0;
}
li {
border-radius: 999px;
background: #eef2ff;
padding: 0.2rem 0.6rem;
}
</style>
给Claude Code的请求要保护组件契约:
请改进src/lib/components/TaskCard.svelte,但保持Svelte 5 runes写法。
要求:
- 不改变Task类型和onToggle签名
- 保留onclick,不要改回旧式on:click
- 保留按钮的aria-pressed
- 只改布局和空数据表现
- 修改后给出一个组件测试建议
这样写的好处是,Claude Code不会顺手把父组件、类型和路由都改掉。Svelte组件越小,AI修改越可控。
共享状态:优先runes,必要时再用stores
Svelte 5允许在 .svelte.ts 文件里使用runes。官方文档说明,这类文件适合复用响应式逻辑或在应用中共享状态。svelte/store 仍然有价值,尤其是复杂异步流、手动订阅、或与已有store生态集成时。
// src/lib/state/taskFilters.svelte.ts
export type TaskStatus = 'all' | 'open' | 'done';
export const taskFilters = $state({
query: '',
status: 'all' as TaskStatus,
tag: ''
});
export function resetTaskFilters() {
taskFilters.query = '';
taskFilters.status = 'all';
taskFilters.tag = '';
}
<!-- src/lib/components/TaskFilterPanel.svelte -->
<script lang="ts">
import { resetTaskFilters, taskFilters } from '$lib/state/taskFilters.svelte';
</script>
<section aria-label="任务筛选">
<label>
关键词
<input bind:value={taskFilters.query} placeholder="发票、文章、评审..." />
</label>
<label>
状态
<select bind:value={taskFilters.status}>
<option value="all">全部</option>
<option value="open">未完成</option>
<option value="done">已完成</option>
</select>
</label>
<button type="button" onclick={resetTaskFilters}>重置</button>
</section>
常见错误是忘记SSR。window、document、localStorage 只在浏览器存在,不能在服务端渲染阶段直接使用。需要浏览器保存时,用 $app/environment 的 browser 判断。
// src/lib/state/theme.svelte.ts
import { browser } from '$app/environment';
export const themeState = $state({
theme: 'system' as 'system' | 'light' | 'dark'
});
export function loadTheme() {
if (!browser) return;
const saved = localStorage.getItem('theme');
if (saved === 'light' || saved === 'dark' || saved === 'system') {
themeState.theme = saved;
}
}
export function saveTheme(nextTheme: typeof themeState.theme) {
themeState.theme = nextTheme;
if (browser) localStorage.setItem('theme', nextTheme);
}
路由、load函数和server边界
SvelteKit的核心是文件路由。src/routes/about 对应 /about,src/routes/tasks/[slug] 对应带 slug 参数的详情页。页面显示前需要数据时,可以在同级目录放 +page.server.ts。数据库、私有API、环境变量等只应该放在 $lib/server 或server-only文件中。
// src/lib/server/tasks.ts
export type Task = {
id: string;
slug: string;
title: string;
done: boolean;
estimateMinutes: number;
tags: string[];
};
const tasks: Task[] = [
{
id: 'task-1',
slug: 'write-svelte-guide',
title: '起草SvelteKit文章',
done: false,
estimateMinutes: 45,
tags: ['writing', 'svelte']
}
];
export async function getTaskBySlug(slug: string) {
return tasks.find((task) => task.slug === slug) ?? null;
}
// src/routes/tasks/[slug]/+page.server.ts
import { error } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
import { getTaskBySlug } from '$lib/server/tasks';
export const load: PageServerLoad = async ({ params }) => {
const task = await getTaskBySlug(params.slug);
if (!task) {
error(404, 'Task not found');
}
return { task };
};
<!-- src/routes/tasks/[slug]/+page.svelte -->
<script lang="ts">
import type { PageProps } from './$types';
let { data }: PageProps = $props();
</script>
<svelte:head>
<title>{data.task.title} | Tasks</title>
</svelte:head>
<article>
<p>{data.task.done ? '已完成' : '未完成'}</p>
<h1>{data.task.title}</h1>
<p>预计: {data.task.estimateMinutes}分钟</p>
</article>
给Claude Code的安全提示词可以这样写:
请读取src/routes/tasks/[slug]和src/lib/server/tasks.ts。
给Task增加dueDate字段,并在详情页显示。
服务端数据访问必须留在$lib/server。
不要重命名[slug]路由目录。
最后运行npm run check。
表单actions与渐进增强
SvelteKit form actions允许 +page.server.ts 导出 actions,普通 <form method="POST"> 就能提交到服务端。JavaScript不可用时也能工作;有JavaScript时再用 use:enhance 改善体验。这对咨询表单、注册表单、内部审批表单都很实用。
// src/routes/contact/+page.server.ts
import { fail } from '@sveltejs/kit';
import type { Actions } from './$types';
export const actions = {
default: async ({ request }) => {
const formData = await request.formData();
const values = {
name: String(formData.get('name') ?? '').trim(),
email: String(formData.get('email') ?? '').trim(),
message: String(formData.get('message') ?? '').trim()
};
const errors: Record<string, string> = {};
if (values.name.length < 2) errors.name = '姓名至少需要2个字符。';
if (!values.email.includes('@')) errors.email = '请确认邮箱地址。';
if (values.message.length < 10) errors.message = '内容至少需要10个字符。';
if (Object.keys(errors).length > 0) {
return fail(400, { values, errors });
}
console.log('New inquiry', values);
return { success: true };
}
} satisfies Actions;
<!-- src/routes/contact/+page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
import type { PageProps } from './$types';
let { form }: PageProps = $props();
</script>
{#if form?.success}
<p role="status">已发送。我们会在1到3个工作日内回复。</p>
{/if}
<form method="POST" use:enhance>
<label>
姓名
<input name="name" value={form?.values?.name ?? ''} aria-invalid={!!form?.errors?.name} />
</label>
{#if form?.errors?.name}<p>{form.errors.name}</p>{/if}
<label>
邮箱
<input name="email" type="email" value={form?.values?.email ?? ''} aria-invalid={!!form?.errors?.email} />
</label>
{#if form?.errors?.email}<p>{form.errors.email}</p>{/if}
<label>
内容
<textarea name="message" rows="5" aria-invalid={!!form?.errors?.message}>{form?.values?.message ?? ''}</textarea>
</label>
{#if form?.errors?.message}<p>{form.errors.message}</p>{/if}
<button type="submit">发送</button>
</form>
不要用 GET 产生副作用,不要把私钥传给浏览器,不要用 {@html} 直接显示未清洗内容。让Claude Code改表单时,明确写上“保留服务端验证”“无JavaScript也能提交”“保留可访问性属性”。
测试、用例和常见坑
Svelte官方测试文档提到,Vite和SvelteKit项目很适合使用Vitest。组件测试可以用Testing Library,端到端流程再交给Playwright。
// src/lib/components/TaskCard.test.ts
import { fireEvent, render, screen } from '@testing-library/svelte';
import { describe, expect, it } from 'vitest';
import TaskCard from './TaskCard.svelte';
describe('TaskCard', () => {
it('toggles the task when the button is clicked', async () => {
let toggledId = '';
render(TaskCard, {
task: {
id: 'task-1',
title: '写SvelteKit文章',
done: false,
estimateMinutes: 45,
tags: ['writing']
},
onToggle: (id) => {
toggledId = id;
}
});
await fireEvent.click(screen.getByRole('button', { name: '标为完成' }));
expect(toggledId).toBe('task-1');
});
});
npm run check
npm run test
npm run build
git diff -- src/lib src/routes
| 使用场景 | 适合交给Claude Code | 人类必须检查 |
|---|---|---|
| 后台筛选UI | 用 $state 和 $derived 拆分筛选逻辑 | 哪些条件进入URL,哪些字段受权限控制 |
| 博客/CMS详情页 | 连接 [slug]、load、SEO标题和404 | HTML清洗、草稿可见性、预览规则 |
| 咨询或获客表单 | actions、验证、use:enhance、测试 | 隐私、通知目标、反垃圾策略 |
| Svelte 4迁移Svelte 5 | 小范围把 export let 和 $: 改成runes | 不要自动大迁移导致行为变化 |
最常见的坑有五个。第一,需求太大,导致Claude Code同时改路由、UI、数据库和认证。第二,Svelte 4和Svelte 5写法混在一起。第三,从组件中导入 $lib/server 泄露server-only边界。第四,把 $effect 用来做普通计算,产生难追踪循环。第五,只接受“按钮存在”这种弱测试,没有验证点击、错误提示、成功状态和404。
如果你希望把SvelteKit文章、表单和咨询转成收入线索,可以把技术内容自然连接到 Claude Code咨询页面。继续学习时,也建议阅读 Claude Code入门指南、TypeScript技巧 和 测试策略。
参考资料、提示词模板和实际结果
需要确认行为时,请优先看官方资料:Svelte文档、SvelteKit文档、SvelteKit form actions、Vite指南 和 Claude Code文档。
请先读取当前SvelteKit结构,然后只改下面范围。
文件: src/routes/contact/+page.svelte 和 src/routes/contact/+page.server.ts
目标: 给咨询表单增加company字段
约束:
- 保持Svelte 5 runes写法
- 不删除use:enhance
- 增加服务端验证
- 不改现有CTA文案和布局class
- 完成前运行npm run check
最后用3行总结: 改动、风险、还缺的测试。
Masa在一个小型SvelteKit任务应用中试过这套流程后,最明显的收益来自plan mode。先让Claude Code读结构,再限定“保留runes语法、运行 npm run check、不要commit”,最终diff更小,初学者也更容易审查每一处Svelte和SvelteKit改动。
免费 PDF: Claude Code 速查表
输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。
我们会妥善保护你的信息,不发送垃圾邮件。
把 Claude Code 变成真正能带来结果的工作流
先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。
关于作者
Masa
专注 Claude Code 实务流程、团队导入和内容转化的工程师。
相关文章
从Obsidian到CLAUDE.md的Claude Code流程:不再反复解释上下文
把 Obsidian 工作笔记整理成 CLAUDE.md 运行说明,让 Claude Code 每次都带着正确上下文开始。
Claude Code 收入 CTA 路由:从文章分流到 PDF、Gumroad 与咨询
用 Claude Code 按读者意图把文章流量分到免费 PDF、Gumroad 教材或咨询入口。
Claude Code 团队交接规则: 把审查证据、权限、回滚和收入路径一起交付
面向团队的 Claude Code 交接格式: 证据、权限、回滚、免费 PDF、Gumroad 与咨询路径都要可审查。