SvelteKit 内置了强大的表单处理机制。通过 actions 可以在服务端处理表单提交,结合 use:enhance 实现无缝的渐进增强体验。
服务端 Actions
+page.server.ts 中的 actions 对象定义了表单处理函数。
// src/routes/contact/+page.server.ts
import type { Actions } from './$types';
export const actions: Actions = {
default: async ({ request }) => {
const data = await request.formData();
const name = data.get('name') as string;
const email = data.get('email') as string;
const message = data.get('message') as string;
// 表单验证
const errors: Record<string, string> = {};
if (!name || name.length < 2) {
errors.name = '姓名至少 2 个字符';
}
if (!email || !email.includes('@')) {
errors.email = '请输入有效的邮箱地址';
}
if (!message || message.length < 10) {
errors.message = '消息至少 10 个字符';
}
if (Object.keys(errors).length > 0) {
// 返回验证错误,SvelteKit 会自动序列化
return { errors, values: { name, email, message } };
}
// 处理成功逻辑(如发送邮件、存入数据库)
await sendContactEmail({ name, email, message });
// 重定向到成功页
return { success: true };
}
};
对应的表单组件:
<!-- src/routes/contact/+page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
import type { PageData } from './$types';
let { data, form } = $props();
</script>
<h1>联系我们</h1>
{#if form?.success}
<p class="success">消息已发送,我们会尽快回复!</p>
{:else}
<form method="POST" use:enhance>
<label>
姓名
<input name="name" value={form?.values?.name ?? ''} />
{#if form?.errors?.name}
<span class="error">{form.errors.name}</span>
{/if}
</label>
<label>
邮箱
<input name="email" type="email" value={form?.values?.email ?? ''} />
{#if form?.errors?.email}
<span class="error">{form.errors.email}</span>
{/if}
</label>
<label>
消息
<textarea name="message" rows="5">{form?.values?.message ?? ''}</textarea>
{#if form?.errors?.message}
<span class="error">{form.errors.message}</span>
{/if}
</label>
<button type="submit">发送</button>
</form>
{/if}
Named Actions
除了 default 外,可以定义命名 action 来处理不同操作:
export const actions: Actions = {
login: async ({ request, cookies }) => {
const data = await request.formData();
// 登录逻辑...
},
register: async ({ request, cookies }) => {
const data = await request.formData();
// 注册逻辑...
},
logout: async ({ cookies }) => {
cookies.delete('session', { path: '/' });
}
};
前端通过表单的 action 属性区分:
<form method="POST" action="?/login" use:enhance>
<!-- 登录字段 -->
</form>
<form method="POST" action="?/register" use:enhance>
<!-- 注册字段 -->
</form>
use:enhance 渐进增强
use:enhance 是 SvelteKit 提供的一个 action,让传统表单提交获得 SPA 般的体验:
- 表单提交不刷新页面
- 网络请求通过 fetch 发送
- 服务器返回的数据自动更新
form属性 - 提交期间可显示加载状态
<script lang="ts">
import { enhance } from '$app/forms';
</script>
<form
method="POST"
use:enhance={({ form, data, action, cancel }) => {
// 提交前回调:可在此显示加载状态
document.getElementById('submit-btn')!.disabled = true;
return async ({ result, update }) => {
// result.type 可能是 'success' | 'error' | 'redirect'
if (result.type === 'success') {
// 手动调用 update() 更新 form 数据
update();
// 重置表单
form.reset();
}
document.getElementById('submit-btn')!.disabled = false;
};
}}
>
<input name="query" />
<button id="submit-btn" type="submit">搜索</button>
</form>
API 端点 (+server.ts)
除了页面路由,SvelteKit 还支持纯 API 端点。在路由目录下创建 +server.ts,导出 HTTP 方法处理函数。
// src/routes/api/posts/[slug]/+server.ts
import type { RequestHandler } from './$types';
import { json, error } from '@sveltejs/kit';
export const GET: RequestHandler = async ({ params, url }) => {
const slug = params.slug;
const post = await getPost(slug);
if (!post) {
throw error(404, '文章不存在');
}
return json(post);
};
export const POST: RequestHandler = async ({ request, params }) => {
const body = await request.json();
const slug = params.slug;
// 更新文章
await updatePost(slug, body);
return json({ success: true });
};
export const DELETE: RequestHandler = async ({ params }) => {
await deletePost(params.slug);
return new Response(null, { status: 204 });
};
接收 JSON 请求
通过 request.json() 解析请求体,配合前端 fetch 使用:
export const POST: RequestHandler = async ({ request }) => {
const { title, content } = await request.json();
if (!title || !content) {
throw error(400, '标题和内容不能为空');
}
const post = await createPost({ title, content });
return json(post, { status: 201 });
};
综合示例:Todo 列表
一个完整的 Todo 应用展示 actions 和 API 的结合:
+page.server.ts:
import type { Actions, PageServerLoad } from './$types';
let todos: Array<{ id: number; text: string; done: boolean }> = [];
let nextId = 1;
export const load: PageServerLoad = async () => {
return { todos };
};
export const actions: Actions = {
add: async ({ request }) => {
const data = await request.formData();
const text = data.get('text') as string;
if (!text || text.length < 1) {
return { error: '请输入待办事项' };
}
todos = [...todos, { id: nextId++, text, done: false }];
return { todos };
},
toggle: async ({ request }) => {
const data = await request.formData();
const id = Number(data.get('id'));
todos = todos.map(t => t.id === id ? { ...t, done: !t.done } : t);
return { todos };
}
};
+page.svelte:
<script lang="ts">
import { enhance } from '$app/forms';
let { data, form } = $props();
let items = $derived(form?.todos ?? data.todos);
</script>
<h1>Todo 列表</h1>
<form method="POST" action="?/add" use:enhance>
<input name="text" placeholder="新增待办..." />
<button type="submit">添加</button>
</form>
<ul>
{#each items as item}
<li>
<form method="POST" action="?/toggle" use:enhance>
<input type="hidden" name="id" value={item.id} />
<button type="submit">
{item.done ? '☑' : '☐'} {item.text}
</button>
</form>
</li>
{/each}
</ul>
总结
本章你学会了:
- 使用
+page.server.ts的actions处理表单提交 - Named actions 处理不同表单类型
use:enhance实现无刷新渐进增强+server.ts创建 RESTful API 端点- 完整表单验证和错误回显
下一章将介绍部署方案和进阶优化技巧。