sveltekit
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSvelteKit Core Knowledge
SvelteKit 核心知识
Full Reference: See advanced.md for WebSocket integration, Server-Sent Events, Socket.IO, hybrid form actions, room management, and streaming patterns.
Deep Knowledge: Usewith technology:mcp__documentation__fetch_docsfor comprehensive documentation.sveltekit
完整参考:WebSocket 集成、Server-Sent Events、Socket.IO、混合 form actions、房间管理和流式传输模式相关内容请查看 advanced.md。
深度知识:如需获取完整文档,请调用并指定 technology 参数为mcp__documentation__fetch_docs。sveltekit
Route Structure
路由结构
src/routes/
├── +page.svelte → / (page UI)
├── +page.server.ts → / (server load/actions)
├── +layout.svelte → (shared layout)
├── users/
│ ├── +page.svelte → /users
│ └── [id]/
│ └── +page.svelte → /users/:idsrc/routes/
├── +page.svelte → / (page UI)
├── +page.server.ts → / (server load/actions)
├── +layout.svelte → (shared layout)
├── users/
│ ├── +page.svelte → /users
│ └── [id]/
│ └── +page.svelte → /users/:idKey Concepts
核心概念
- → UI component
+page.svelte - → Server-only code
+page.server.ts - → Universal (runs both)
+page.ts - → Nested layouts
+layout - → Error pages
+error.svelte
- → UI 组件
+page.svelte - → 仅服务端运行的代码
+page.server.ts - → 通用代码(客户端和服务端均会运行)
+page.ts - → 嵌套布局
+layout - → 错误页面
+error.svelte
Load Functions
Load Functions
ts
// +page.server.ts
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params, fetch }) => {
const user = await db.users.find(params.id);
return { user };
};svelte
<!-- +page.svelte -->
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<h1>{data.user.name}</h1>ts
// +page.server.ts
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params, fetch }) => {
const user = await db.users.find(params.id);
return { user };
};svelte
<!-- +page.svelte -->
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<h1>{data.user.name}</h1>Form Actions
Form Actions
ts
// +page.server.ts
import type { Actions } from './$types';
export const actions: Actions = {
default: async ({ request }) => {
const data = await request.formData();
await db.users.create({ name: data.get('name') });
return { success: true };
},
delete: async ({ params }) => {
await db.users.delete(params.id);
}
};svelte
<form method="POST">
<input name="name" />
<button>Create</button>
</form>
<form method="POST" action="?/delete">
<button>Delete</button>
</form>ts
// +page.server.ts
import type { Actions } from './$types';
export const actions: Actions = {
default: async ({ request }) => {
const data = await request.formData();
await db.users.create({ name: data.get('name') });
return { success: true };
},
delete: async ({ params }) => {
await db.users.delete(params.id);
}
};svelte
<form method="POST">
<input name="name" />
<button>创建</button>
</form>
<form method="POST" action="?/delete">
<button>删除</button>
</form>Form Validation with Zod
基于 Zod 的表单验证
typescript
// src/routes/users/+page.server.ts
import { fail } from '@sveltejs/kit';
import { z } from 'zod';
const UserSchema = z.object({
email: z.string().email('Invalid email'),
name: z.string().min(2, 'Name too short'),
});
export const actions = {
create: async ({ request }) => {
const formData = await request.formData();
const data = Object.fromEntries(formData);
const result = UserSchema.safeParse(data);
if (!result.success) {
return fail(400, {
errors: result.error.flatten().fieldErrors,
data,
});
}
await createUser(result.data);
return { success: true };
},
};svelte
<!-- +page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
export let form;
</script>
<form method="POST" action="?/create" use:enhance>
<input name="email" value={form?.data?.email ?? ''} />
{#if form?.errors?.email}
<span class="error">{form.errors.email[0]}</span>
{/if}
<button>Create</button>
</form>typescript
// src/routes/users/+page.server.ts
import { fail } from '@sveltejs/kit';
import { z } from 'zod';
const UserSchema = z.object({
email: z.string().email('无效邮箱地址'),
name: z.string().min(2, '名称过短'),
});
export const actions = {
create: async ({ request }) => {
const formData = await request.formData();
const data = Object.fromEntries(formData);
const result = UserSchema.safeParse(data);
if (!result.success) {
return fail(400, {
errors: result.error.flatten().fieldErrors,
data,
});
}
await createUser(result.data);
return { success: true };
},
};svelte
<!-- +page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
export let form;
</script>
<form method="POST" action="?/create" use:enhance>
<input name="email" value={form?.data?.email ?? ''} />
{#if form?.errors?.email}
<span class="error">{form.errors.email[0]}</span>
{/if}
<button>创建</button>
</form>Security Configuration
安全配置
typescript
// svelte.config.js
export default {
kit: {
csrf: { checkOrigin: true },
csp: {
directives: {
'default-src': ['self'],
'script-src': ['self'],
},
},
},
};
// src/hooks.server.ts
import type { Handle } from '@sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => {
const response = await resolve(event);
response.headers.set('X-Frame-Options', 'DENY');
response.headers.set('X-Content-Type-Options', 'nosniff');
return response;
};typescript
// svelte.config.js
export default {
kit: {
csrf: { checkOrigin: true },
csp: {
directives: {
'default-src': ['self'],
'script-src': ['self'],
},
},
},
};
// src/hooks.server.ts
import type { Handle } from '@sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => {
const response = await resolve(event);
response.headers.set('X-Frame-Options', 'DENY');
response.headers.set('X-Content-Type-Options', 'nosniff');
return response;
};Authentication
身份认证
typescript
// src/hooks.server.ts
import type { Handle } from '@sveltejs/kit';
import { redirect } from '@sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => {
const session = event.cookies.get('session');
if (session) {
event.locals.user = await verifySession(session);
}
// Protect routes
if (event.url.pathname.startsWith('/admin')) {
if (!event.locals.user?.isAdmin) {
throw redirect(303, '/login');
}
}
return resolve(event);
};typescript
// src/hooks.server.ts
import type { Handle } from '@sveltejs/kit';
import { redirect } from '@sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => {
const session = event.cookies.get('session');
if (session) {
event.locals.user = await verifySession(session);
}
// 路由保护
if (event.url.pathname.startsWith('/admin')) {
if (!event.locals.user?.isAdmin) {
throw redirect(303, '/login');
}
}
return resolve(event);
};Error Handling
错误处理
svelte
<!-- src/routes/+error.svelte -->
<script lang="ts">
import { page } from '$app/stores';
</script>
<div class="error">
<h1>{$page.status}</h1>
<p>{$page.error?.message}</p>
<a href="/">Go home</a>
</div>typescript
// src/hooks.server.ts
import type { HandleServerError } from '@sveltejs/kit';
export const handleError: HandleServerError = async ({ error, event }) => {
const errorId = crypto.randomUUID();
console.error('Server error:', { errorId, error, url: event.url });
return {
message: 'An unexpected error occurred',
errorId,
};
};svelte
<!-- src/routes/+error.svelte -->
<script lang="ts">
import { page } from '$app/stores';
</script>
<div class="error">
<h1>{$page.status}</h1>
<p>{$page.error?.message}</p>
<a href="/">返回首页</a>
</div>typescript
// src/hooks.server.ts
import type { HandleServerError } from '@sveltejs/kit';
export const handleError: HandleServerError = async ({ error, event }) => {
const errorId = crypto.randomUUID();
console.error('服务端错误:', { errorId, error, url: event.url });
return {
message: '发生了意外错误',
errorId,
};
};Performance
性能优化
typescript
// Prerendering
export const prerender = true;
export const entries = async () => {
const posts = await getPosts();
return posts.map((post) => ({ slug: post.slug }));
};
// Streaming
export const load: PageServerLoad = async () => {
return {
critical: await getCriticalData(),
streamed: {
slow: getSlowData(), // Not awaited
},
};
};svelte
{#await data.streamed.slow}
<Skeleton />
{:then slowData}
<SlowContent data={slowData} />
{/await}typescript
// 预渲染
export const prerender = true;
export const entries = async () => {
const posts = await getPosts();
return posts.map((post) => ({ slug: post.slug }));
};
// 流式传输
export const load: PageServerLoad = async () => {
return {
critical: await getCriticalData(),
streamed: {
slow: getSlowData(), // 不等待执行完成
},
};
};svelte
{#await data.streamed.slow}
<Skeleton />
{:then slowData}
<SlowContent data={slowData} />
{/await}When NOT to Use This Skill
本技能不适用场景
- Svelte without SvelteKit: Use skill
frontend-svelte - Next.js: Use skill
nextjs-app-router - Nuxt: Use skill
nuxt3 - Remix: Use skill
remix
- 不含 SvelteKit 的纯 Svelte 开发:使用 技能
frontend-svelte - Next.js 开发:使用 技能
nextjs-app-router - Nuxt 开发:使用 技能
nuxt3 - Remix 开发:使用 技能
remix
Anti-Patterns
反模式
| Anti-Pattern | Why It's Wrong | Correct Approach |
|---|---|---|
| Fetching in onMount | Client-side only, no SSR | Use load function |
| Using +page.ts for secrets | Exposed to client | Use +page.server.ts |
| Not using use:enhance | No progressive enhancement | Add use:enhance to forms |
| Ignoring form validation | Security risk | Validate with Zod |
| No +error.svelte | Poor error UX | Create error page |
| Not setting CSRF protection | Security vulnerability | Enable csrf.checkOrigin |
| 反模式 | 问题原因 | 正确方案 |
|---|---|---|
| 在 onMount 中发起请求 | 仅客户端执行,无 SSR 效果 | 使用 load function |
| 在 +page.ts 中存放敏感信息 | 会暴露给客户端 | 使用 +page.server.ts |
| 不使用 use:enhance | 无渐进式增强能力 | 给表单添加 use:enhance |
| 忽略表单验证 | 存在安全风险 | 使用 Zod 做验证 |
| 没有 +error.svelte | 用户错误体验差 | 创建错误页面 |
| 不开启 CSRF 防护 | 存在安全漏洞 | 启用 csrf.checkOrigin |
Quick Troubleshooting
快速故障排查
| Issue | Possible Cause | Solution |
|---|---|---|
| "Cannot access event.locals.user" | Not set in hooks | Set in handle hook |
| Form action not triggered | Missing action attribute | Use action="?/actionName" |
| Data undefined in load | Not exported | Export const load |
| CSRF error | checkOrigin enabled | Check csrf settings |
| Hydration error | Server/client differ | Use browser check |
| Redirect doesn't work | Wrong status code | Use redirect(303, '/path') |
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| "Cannot access event.locals.user" | 未在 hooks 中设置 | 在 handle 钩子中配置 |
| Form action 未触发 | 缺少 action 属性 | 使用 action="?/actionName" |
| load 中数据为 undefined | 未导出配置 | 导出 const load |
| CSRF 错误 | checkOrigin 已开启 | 检查 csrf 配置 |
| hydration 错误 | 服务端/客户端渲染结果不一致 | 增加浏览器环境判断 |
| 重定向不生效 | 状态码错误 | 使用 redirect(303, '/path') |
Production Checklist
生产环境检查清单
- CSP headers configured
- CSRF protection enabled
- Error handling (+error.svelte, hooks)
- Auth in hooks.server.ts
- Form validation with Zod
- use:enhance for progressive forms
- Prerendering static routes
- Streaming for slow data
- 已配置 CSP 头
- 已开启 CSRF 防护
- 已配置错误处理(+error.svelte、hooks)
- 已在 hooks.server.ts 中实现身份认证
- 已使用 Zod 做表单验证
- 已为表单添加 use:enhance 实现渐进式增强
- 已为静态路由配置预渲染
- 已为慢数据配置流式传输
Reference Documentation
参考文档
- Route Structure
- Load Functions
- 路由结构
- Load Functions