sveltekit
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSvelteKit Full-Stack Development
SvelteKit全栈开发
Overview
概述
SvelteKit is the official full-stack framework built on top of Svelte. It provides file-based routing, server-side rendering (SSR), static site generation (SSG), API routes, and progressive form actions — all with Svelte's compile-time reactivity model that ships zero runtime overhead to the browser. Use this skill when building fast, modern web apps where both DX and performance matter.
SvelteKit是基于Svelte构建的官方全栈框架。它提供基于文件的路由、服务器端渲染(SSR)、静态站点生成(SSG)、API路由和渐进式表单操作——所有这些都采用Svelte的编译时响应式模型,不会向浏览器发送任何运行时开销。当构建注重开发者体验(DX)和性能的快速现代Web应用时,请使用此技能。
When to Use This Skill
何时使用此技能
- Use when building a new full-stack web application with Svelte
- Use when you need SSR or SSG with fine-grained control per route
- Use when migrating a SPA to a framework with server capabilities
- Use when working on a project that needs file-based routing and collocated API endpoints
- Use when the user asks about ,
+page.svelte,+layout.sveltefunctions, or form actionsload
- 当使用Svelte构建新的全栈Web应用时
- 当你需要对每个路由进行细粒度控制的SSR或SSG时
- 当将单页应用(SPA)迁移到具备服务器能力的框架时
- 当处理需要基于文件的路由和并置API端点的项目时
- 当用户询问、
+page.svelte、+layout.svelte函数或表单操作时load
How It Works
工作原理
Step 1: Project Setup
步骤1:项目搭建
bash
npm create svelte@latest my-app
cd my-app
npm install
npm run devChoose Skeleton project + TypeScript + ESLint/Prettier when prompted.
Directory structure after scaffolding:
src/
routes/
+page.svelte ← Root page component
+layout.svelte ← Root layout (wraps all pages)
+error.svelte ← Error boundary
lib/
server/ ← Server-only code (never bundled to client)
components/ ← Shared components
app.html ← HTML shell
static/ ← Static assetsbash
npm create svelte@latest my-app
cd my-app
npm install
npm run dev提示时选择Skeleton project + TypeScript + ESLint/Prettier。
搭建后的目录结构:
src/
routes/
+page.svelte ← 根页面组件
+layout.svelte ← 根布局(包裹所有页面)
+error.svelte ← 错误边界
lib/
server/ ← 仅服务器端代码(永远不会打包到客户端)
components/ ← 共享组件
app.html ← HTML外壳
static/ ← 静态资源Step 2: File-Based Routing
步骤2:基于文件的路由
Every file in maps directly to a URL:
+page.sveltesrc/routes/src/routes/+page.svelte → /
src/routes/about/+page.svelte → /about
src/routes/blog/[slug]/+page.svelte → /blog/:slug
src/routes/shop/[...path]/+page.svelte → /shop/* (catch-all)Route groups (no URL segment): wrap in folder.
Private routes (not accessible as URLs): prefix with or .
(group)/_(group)src/routes/+page.sveltesrc/routes/+page.svelte → /
src/routes/about/+page.svelte → /about
src/routes/blog/[slug]/+page.svelte → /blog/:slug
src/routes/shop/[...path]/+page.svelte → /shop/*(通配捕获)路由组(无URL段):包裹在文件夹中。
私有路由(无法通过URL访问):前缀为或。
(group)/_(group)Step 3: Loading Data with load
Functions
load步骤3:使用load
函数加载数据
loadUse a (universal) or (server-only) file alongside the page:
+page.ts+page.server.tstypescript
// src/routes/blog/[slug]/+page.server.ts
import { error } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params, fetch }) => {
const post = await fetch(`/api/posts/${params.slug}`).then(r => r.json());
if (!post) {
error(404, 'Post not found');
}
return { post };
};svelte
<!-- src/routes/blog/[slug]/+page.svelte -->
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<h1>{data.post.title}</h1>
<article>{@html data.post.content}</article>在页面旁创建(通用)或(仅服务器端)文件:
+page.ts+page.server.tstypescript
// src/routes/blog/[slug]/+page.server.ts
import { error } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params, fetch }) => {
const post = await fetch(`/api/posts/${params.slug}`).then(r => r.json());
if (!post) {
error(404, 'Post not found');
}
return { post };
};svelte
<!-- src/routes/blog/[slug]/+page.svelte -->
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<h1>{data.post.title}</h1>
<article>{@html data.post.content}</article>Step 4: API Routes (Server Endpoints)
步骤4:API路由(服务器端点)
Create files for REST-style endpoints:
+server.tstypescript
// src/routes/api/posts/+server.ts
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async ({ url }) => {
const limit = Number(url.searchParams.get('limit') ?? 10);
const posts = await db.post.findMany({ take: limit });
return json(posts);
};
export const POST: RequestHandler = async ({ request }) => {
const body = await request.json();
const post = await db.post.create({ data: body });
return json(post, { status: 201 });
};创建文件以实现REST风格的端点:
+server.tstypescript
// src/routes/api/posts/+server.ts
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async ({ url }) => {
const limit = Number(url.searchParams.get('limit') ?? 10);
const posts = await db.post.findMany({ take: limit });
return json(posts);
};
export const POST: RequestHandler = async ({ request }) => {
const body = await request.json();
const post = await db.post.create({ data: body });
return json(post, { status: 201 });
};Step 5: Form Actions
步骤5:表单操作
Form actions are the SvelteKit-native way to handle mutations — no client-side fetch required:
typescript
// src/routes/contact/+page.server.ts
import { fail, redirect } from '@sveltejs/kit';
import type { Actions } from './$types';
export const actions: Actions = {
default: async ({ request }) => {
const data = await request.formData();
const email = data.get('email');
if (!email) {
return fail(400, { email, missing: true });
}
await sendEmail(String(email));
redirect(303, '/thank-you');
}
};svelte
<!-- src/routes/contact/+page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
import type { ActionData } from './$types';
export let form: ActionData;
</script>
<form method="POST" use:enhance>
<input name="email" type="email" />
{#if form?.missing}<p class="error">Email is required</p>{/if}
<button type="submit">Subscribe</button>
</form>表单操作是SvelteKit原生的突变处理方式——无需客户端fetch:
typescript
// src/routes/contact/+page.server.ts
import { fail, redirect } from '@sveltejs/kit';
import type { Actions } from './$types';
export const actions: Actions = {
default: async ({ request }) => {
const data = await request.formData();
const email = data.get('email');
if (!email) {
return fail(400, { email, missing: true });
}
await sendEmail(String(email));
redirect(303, '/thank-you');
}
};svelte
<!-- src/routes/contact/+page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
import type { ActionData } from './$types';
export let form: ActionData;
</script>
<form method="POST" use:enhance>
<input name="email" type="email" />
{#if form?.missing}<p class="error">邮箱是必填项</p>{/if}
<button type="submit">订阅</button>
</form>Step 6: Layouts and Nested Routes
步骤6:布局与嵌套路由
svelte
<!-- src/routes/+layout.svelte -->
<script lang="ts">
import type { LayoutData } from './$types';
export let data: LayoutData;
</script>
<nav>
<a href="/">Home</a>
<a href="/blog">Blog</a>
{#if data.user}
<a href="/dashboard">Dashboard</a>
{/if}
</nav>
<slot /> <!-- child page renders here -->typescript
// src/routes/+layout.server.ts
import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = async ({ locals }) => {
return { user: locals.user ?? null };
};svelte
<!-- src/routes/+layout.svelte -->
<script lang="ts">
import type { LayoutData } from './$types';
export let data: LayoutData;
</script>
<nav>
<a href="/">首页</a>
<a href="/blog">博客</a>
{#if data.user}
<a href="/dashboard">控制台</a>
{/if}
</nav>
<slot /> <!-- 子页面在此渲染 -->typescript
// src/routes/+layout.server.ts
import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = async ({ locals }) => {
return { user: locals.user ?? null };
};Step 7: Rendering Modes
步骤7:渲染模式
Control per-route rendering with page options:
typescript
// src/routes/docs/+page.ts
export const prerender = true; // Static — generated at build time
export const ssr = true; // Default — rendered on server per request
export const csr = false; // Disable client-side hydration entirely通过页面选项控制每个路由的渲染方式:
typescript
// src/routes/docs/+page.ts
export const prerender = true; // 静态——构建时生成
export const ssr = true; // 默认——每次请求在服务器端渲染
export const csr = false; // 完全禁用客户端水合Examples
示例
Example 1: Protected Dashboard Route
示例1:受保护的控制台路由
typescript
// src/routes/dashboard/+layout.server.ts
import { redirect } from '@sveltejs/kit';
import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = async ({ locals }) => {
if (!locals.user) {
redirect(303, '/login');
}
return { user: locals.user };
};typescript
// src/routes/dashboard/+layout.server.ts
import { redirect } from '@sveltejs/kit';
import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = async ({ locals }) => {
if (!locals.user) {
redirect(303, '/login');
}
return { user: locals.user };
};Example 2: Hooks — Session Middleware
示例2:钩子——会话中间件
typescript
// src/hooks.server.ts
import type { Handle } from '@sveltejs/kit';
import { verifyToken } from '$lib/server/auth';
export const handle: Handle = async ({ event, resolve }) => {
const token = event.cookies.get('session');
if (token) {
event.locals.user = await verifyToken(token);
}
return resolve(event);
};typescript
// src/hooks.server.ts
import type { Handle } from '@sveltejs/kit';
import { verifyToken } from '$lib/server/auth';
export const handle: Handle = async ({ event, resolve }) => {
const token = event.cookies.get('session');
if (token) {
event.locals.user = await verifyToken(token);
}
return resolve(event);
};Example 3: Preloading and Invalidation
示例3:预加载与失效
svelte
<script lang="ts">
import { invalidateAll } from '$app/navigation';
async function refresh() {
await invalidateAll(); // re-runs all load functions on the page
}
</script>
<button on:click={refresh}>Refresh</button>svelte
<script lang="ts">
import { invalidateAll } from '$app/navigation';
async function refresh() {
await invalidateAll(); // 重新运行页面上的所有load函数
}
</script>
<button on:click={refresh}>刷新</button>Best Practices
最佳实践
- ✅ Use for database/auth logic — it never ships to the client
+page.server.ts - ✅ Use for shared server-only modules (DB client, auth helpers)
$lib/server/ - ✅ Use form actions for mutations instead of client-side — works without JS
fetch - ✅ Type all return values with generated
load($types,PageData)LayoutData - ✅ Use in hooks to pass server-side context to load functions
event.locals - ❌ Don't import server-only code in or
+page.sveltedirectly+layout.svelte - ❌ Don't store sensitive state in stores — use on the server
locals - ❌ Don't skip on forms — without it, forms lose progressive enhancement
use:enhance
- ✅ 使用处理数据库/认证逻辑——它永远不会发送到客户端
+page.server.ts - ✅ 使用存放共享的仅服务器端模块(数据库客户端、认证助手)
$lib/server/ - ✅ 使用表单操作处理突变,而非客户端——无JS环境下也能工作
fetch - ✅ 使用生成的(
$types、PageData)为所有LayoutData返回值添加类型load - ✅ 在钩子中使用将服务器端上下文传递给load函数
event.locals - ❌ 不要在或
+page.svelte中直接导入仅服务器端代码+layout.svelte - ❌ 不要在存储中存储敏感状态——在服务器端使用
locals - ❌ 不要在表单上跳过——没有它,表单会失去渐进式增强能力
use:enhance
Security & Safety Notes
安全注意事项
- All code in ,
+page.server.ts, and+server.tsruns exclusively on the server — safe for DB queries, secrets, and session validation.$lib/server/ - Always validate and sanitize form data before database writes.
- Use or
error(403)fromredirect(303)rather than returning raw error objects.@sveltejs/kit - Set and
httpOnly: trueon all auth cookies.secure: true - CSRF protection is built-in for form actions — do not disable in production.
checkOrigin
- 、
+page.server.ts和+server.ts中的所有代码仅在服务器端运行——可安全用于数据库查询、密钥和会话验证。$lib/server/ - 在写入数据库前,始终验证并清理表单数据。
- 使用中的
@sveltejs/kit或error(403),而非返回原始错误对象。redirect(303) - 为所有认证Cookie设置和
httpOnly: true。secure: true - 表单操作内置CSRF保护——生产环境中不要禁用。
checkOrigin
Common Pitfalls
常见陷阱
-
Problem:in
Cannot use import statement in a moduleSolution: The file must be+page.server.tsor.ts, not.js. Server files and Svelte components are separate..svelte -
Problem: Store value ison first SSR render Solution: Populate the store from the
undefinedfunction return value (loadprop), not from client-sidedata.onMount -
Problem: Form action does not redirect after submit Solution: Usefrom
redirect(303, '/path'), not a plain@sveltejs/kit. 303 is required for POST redirects.return -
Problem:is undefined inside a
locals.userload function Solution: Set+page.server.tsinevent.locals.userbefore thesrc/hooks.server.tscall.resolve()
-
问题:中出现
+page.server.ts错误 解决方案: 文件必须是Cannot use import statement in a module或.ts,而非.js。服务器文件与Svelte组件是分离的。.svelte -
问题: 首次SSR渲染时存储值为解决方案: 从
undefined函数的返回值(load属性)填充存储,而非客户端data。onMount -
问题: 表单提交后未重定向 解决方案: 使用中的
@sveltejs/kit,而非普通redirect(303, '/path')。POST重定向需要使用303状态码。return -
问题:的load函数中
+page.server.ts未定义 解决方案: 在locals.user的src/hooks.server.ts调用前设置resolve()。event.locals.user
Related Skills
相关技能
- — When you prefer React over Svelte for SSR/SSG
@nextjs-app-router-patterns - — Add end-to-end type safety to SvelteKit API routes
@trpc-fullstack - — Authentication patterns usable with SvelteKit hooks
@auth-implementation-patterns - — Styling SvelteKit apps with Tailwind CSS
@tailwind-patterns
- —— 当你更喜欢用React而非Svelte进行SSR/SSG时
@nextjs-app-router-patterns - —— 为SvelteKit API路由添加端到端类型安全
@trpc-fullstack - —— 可用于SvelteKit钩子的认证模式
@auth-implementation-patterns - —— 使用Tailwind CSS为SvelteKit应用设置样式
@tailwind-patterns
Limitations
局限性
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
- 仅当任务明确符合上述描述的范围时才使用此技能。
- 不要将输出视为特定环境验证、测试或专家评审的替代品。
- 如果缺少所需输入、权限、安全边界或成功标准,请停止并请求澄清。