关于 sveltekit
面向开发者和编码 Agent 的 SvelteKit 应用级开发指南,聚焦服务端边界、数据归属、安全与渐进增强。
主要功能
- 01路由与运行时边界
- 02服务端数据加载
- 03原生表单与 actions
- 04明确的身份验证检查
- 05SSR 与水合指导
适用场景
- 01选择合适的 SvelteKit 路由文件
- 02检查服务端数据与机密的流向
- 03构建无 JavaScript 也可用的表单
- 04检查页面和端点的身份验证
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ joshuadavidthomas/ agent-skills --skill sveltekit安装后在 Claude Code 中输入/sveltekit确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.mdreferences/auth.mdbetter-auth.mdcloudflare.mderrors-and-redirects.mdfile-naming.mdform-actions.mdforms-validation.mdlayout-patterns.mdload-functions.mdremote-functions.mdserialization.mdssr-hydration.md详细介绍
在 SvelteKit 中思考
SvelteKit 不只是带有文件夹的 Svelte。它是一套应用边界系统:路由文件决定数据的归属,服务端代码留在服务端,表单提交支持渐进式增强,重定向和错误都是控制流的一部分。
核心失败模式:把 SvelteKit 写成带有零散服务端辅助代码的客户端 SPA。从组件中获取服务端负责的数据。把身份验证藏在钩子中,并假设布局能保护端点。从 load 返回类实例。调用 redirect() 却不抛出。明明原生表单配合 actions 就能实现,却构建只能依赖 JavaScript 的表单。这些做法在 SSR、渐进式增强、缓存或安全边界变得重要之前都行得通。
将此技能用于应用级 SvelteKit 决策。组件内部细节请使用 svelte5:runes、snippets、组件 props、DOM 事件、无障碍性、过渡和组件状态。
SvelteKit 的思考方式
路由负责应用边界
1. 文件名决定行为。 +page.svelte、+layout.server.ts、+page.server.ts 和 +server.ts 并非只是组织代码的细节;它们决定代码在哪里运行,以及谁可以调用它。参见 references/file-naming.md。
2. 路由组组织策略,而非 URL。 (app) 和 (auth) 用于划分布局、身份验证和数据边界,而不会改变路径。将受保护的路由放在受保护的布局下;登录和注册页面则放在该组之外。参见 references/layout-patterns.md。
3. 布局保护页面,不保护端点。 受保护的 +layout.server.ts 会限制对子页面的访问,但不会保护同级或子级的 +server.ts 端点;每个端点都必须自行执行身份验证。参见 references/auth.md。
数据流应体现归属
4. 服务端负责的数据应在服务端加载。 数据库查询、密钥、私有 API 和基于会话的数据应放在 +page.server.ts / +layout.server.ts 中,而不是组件的 onMount 或浏览器端 fetch 中。参见 references/load-functions.md。
// ❌ component fetches server-owned data after mount
onMount(async () => { user = await (await fetch('/api/me')).json(); });
// ✅ +page.server.ts — server owns it, page receives data
export const load = async ({ locals }) => ({ user: await locals.getUser() });
5. 通用 load 只用于通用任务。 仅当代码能在浏览器和服务端安全运行时,才使用 +page.ts。如果代码需要密钥或受信任的身份验证,它就不是通用代码。
6. Load 返回数据,而非行为。 服务端 load 应返回可序列化的数据。不要返回类实例、函数、数据库句柄或依赖对象身份的复杂对象。参见 references/serialization.md。
7. 重定向和错误是通过抛出实现的控制流。 在 SvelteKit 2 中,redirect() 和 error() 会返回对象;要抛出这些对象。只调用而不抛出是 bug。参见 references/errors-and-redirects.md。
// ❌ bare call — execution continues, the redirect never happens
if (!locals.user) redirect(303, '/login');
// ✅ throw it
if (!locals.user) throw redirect(303, '/login');
表单优先考虑服务端
8. 表单 actions 是默认的变更边界。 如果用户提交的是表单格式的数据,应优先使用原生表单和 +page.server.ts action,而不是另行设计仅客户端的变更路径。参见 references/form-actions.md。
9. 渐进式增强是增强,而非前提。 表单应能在没有 JavaScript 的情况下正常工作;use:enhance 用于改善等待状态、焦点、失效处理和用户体验。它不应成为变更操作唯一的生效方式。
10. 在边界处进行验证。 在服务端验证,使用 fail 返回字段形式的错误,并让组件显示这些错误。如果项目使用 shadcn-svelte 或 bits-ui,应通过 Field.* 组件呈现这些错误,而不是使用临时拼凑的封装。参见 references/forms-validation.md。
// +page.server.ts action — validate server-side, return field-shaped errors
if (!email) return fail(400, { errors: { email: 'Email is required' } });
认证明确而简单
11. Hook 填充上下文;路由执行策略。 hooks.server.ts 应解析会话并填充 locals。布局/服务端加载函数和端点决定允许执行的操作。如果基础设施缺失,不要静默跳过认证。参见 references/auth.md。
12. Cookie 和 locals 是请求状态。 将它们视为每个请求独有的服务端数据。不要将其镜像到全局模块状态,也不要信任客户端副本来进行授权。
13. API 端点有自己的安全边界。 在每个 +server.ts 中检查方法、认证、授权、输入验证和响应结构。页面布局与直接 HTTP 调用无关。
远程函数仍是服务端边界
14. 远程函数不会取消验证。 command、query 和 form 仍会跨越客户端/服务端边界。验证参数、检查认证,并返回可序列化数据。远程函数取决于版本/功能标志;在推荐使用前,确认项目已选择启用。参见 references/remote-functions.md。
15. 根据意图选择远程原语。 使用 query 进行重复读取,使用 command 执行命令式变更,使用 form 处理表单形式的提交。没有充分理由时,不要用远程函数绕过 SvelteKit 的常规页面/表单模型。
SSR 是默认环境
16. 浏览器 API 需有条件地使用。 SSR 期间不存在 window、document、localStorage、媒体 API 和 DOM 测量功能。谨慎使用组件副作用、browser 或仅限客户端的边界。参见 references/ssr-hydration.md。
17. Hydration 不匹配是设计反馈。 如果服务端和客户端渲染的初始标记不同,请修复数据边界,或有意延迟仅限浏览器的渲染。不要用随机客户端检查来掩盖不匹配。
处理变更
由外向内逐步处理——先确定每个边界,再继续下一步:
- 运行时与路由边界。 确定有哪些
+文件,以及每个文件在哪里运行(服务端 / 通用 / 端点)。→ 确认机密信息和数据库访问不会进入+page.ts或组件。 - 数据归属。 服务端拥有的数据放在
+*.server.ts中;load返回可序列化数据。→ 确认没有类实例或函数跨越load。 - 变更。 表单形式的数据通过原生表单和
+page.server.tsaction 处理。→ 添加use:enhance前,确认禁用 JavaScript 时仍可正常工作。 - 认证。 Hook 填充
locals;每个页面加载函数和每个+server.ts都执行策略并默认拒绝。→ 对每个端点测试直接 HTTP 调用,而不只是测试页面。 - SSR。 对浏览器 API 进行门控;初始标记保持一致。→ 关闭 JS 进行渲染,并确认没有 hydration 不匹配。
- 发现问题时的处理顺序: 先处理安全性/正确性问题(认证、序列化、抛出控制流),再处理渐进增强方面的缺失,最后处理易用性问题。
表单 action 流程
- 使用包含真实输入项和标签的原生
<form method="POST">。 +page.server.ts中的actions在服务端验证;输入无效时执行return fail(400, { errors }),成功时执行throw redirect(...)。- 组件渲染
form?.errors(如果项目使用 shadcn-svelte / bits-ui,则通过Field.*渲染)。 - 最后添加
use:enhance——仅用于待处理状态、焦点和失效处理。 - 检查: 禁用 JavaScript 后提交;变更仍应生效。
保护路由
- 将受保护路由放在包含受保护
+layout.server.ts的路由组下;登录/注册页面放在该组之外。 hooks.server.ts将会话解析到locals。- 受保护的
+layout.server.ts对未认证用户执行throw redirect(...)。 - 保护范围内的每个
+server.ts都要自行重新检查认证。 - 检查: 没有会话时,直接请求受保护端点会被拒绝。
常见错误(Agent 失效模式)
- 在
onMount中获取服务器拥有的数据 → 使用服务器load。 - 使用
$effect将查询/页面状态推送到导航中 → 在控件的事件处理程序或表单提交中更新 URL;需要时从data派生显示值。 - 使用通用 load 处理密钥 → 移至
+page.server.ts或+layout.server.ts。 - 从 load 返回类/函数 → 返回可序列化的普通数据。
- 调用
redirect()/error()时不加throw→ 对它们使用throw。 - 通过布局保护端点 → 在每个
+server.ts中检查身份验证。 - 只在 hooks 中处理身份验证策略 → hooks 填充
locals;路由负责执行策略。 - 数据库/会话基础设施缺失时静默跳过身份验证 → 默认拒绝访问。
- 将登录页面放在受保护的路由组中 → 将身份验证页面置于受保护布局之外。
- 构建仅依赖 JS 的表单 → 使用原生表单 + actions + 渐进增强。
- 信任客户端验证 → 在服务器边界进行验证。
- 使用原始包装器渲染 shadcn-svelte 验证 → 在组件中使用
Field.Field、Field.Label和Field.Error。 - 使用远程函数却不进行身份验证/验证 → 将它们视为服务器端点。
- 在 SSR 期间使用浏览器 API → 使用 effects/仅浏览器逻辑进行限制。
- 将 svelte5 组件建议用于应用数据流 → 加载 SvelteKit 参考文档。
快速参考
| 常见问题 | SvelteKit 默认做法 | 参考 |
|---|---|---|
| 不确定选择哪个路由文件 | 根据运行时和边界选择 | file-naming |
| 受保护的页面组 | 在 +layout.server.ts 中检查身份验证 | auth |
| 受保护的端点 | 在 +server.ts 中检查身份验证 | auth |
| 浏览器代码中的数据库/密钥 | server load/action/endpoint | load-functions |
通过 $effect 更新查询/页面控件 | 事件处理程序或表单提交 | load-functions |
| 表单形式的变更操作 | 表单 action | form-actions |
| 字段验证错误 | fail(400, { errors }) | forms-validation |
裸调用 redirect() 或 error() | throw redirect(...) / throw error(...) | errors-and-redirects |
| 从 load 返回复杂对象 | 可序列化的普通数据 | serialization |
| SSR 中仅客户端可用的 API | effect/浏览器环境保护 | ssr-hydration |
| 客户端重复读取服务器数据 | 适用远程函数时使用 query() | remote-functions |
| 从客户端命令式修改服务器数据 | 使用带验证/身份验证的 command() | remote-functions |
| 组件 API/runes 问题 | 使用 svelte5 | svelte5 |
参考索引
文件命名 · 布局模式 · Load 函数 · 表单 Actions · 表单验证 · 身份验证 · 错误/重定向 · 序列化 · SSR/Hydration · 远程函数 · Better Auth · Cloudflare