关于 sveltekit-advanced
sveltekit-advanced 是面向 SvelteKit 开发者的进阶功能参考技能,涵盖服务端与客户端状态、远程函数、钩子、环境变量、错误处理及路由状态管理。
主要功能
- 01服务端与客户端状态指导
- 02远程函数 API 参考
- 03环境变量与钩子配置
- 04错误处理模式
- 05Service Worker 与服务端专用模块
- 06快照与浅层路由
适用场景
- 01选择应用状态的存放位置
- 02构建类型安全的客户端/服务端调用
- 03配置请求钩子和错误响应
- 04防止浏览器端导入机密内容
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ full-stack-skills/ svelte-skills --skill sveltekit-advanced安装后在 Claude Code 中输入/sveltekit-advanced确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
agents/openai.yamlexamples/app-modules.mdenv-vars.mderrors.mdhooks.mdlink-options.mdREADME.mdremote-functions.mdservice-worker.mdshallow-snapshots.mdstate-management.mdreferences/app-modules.mdenv-vars.mderrors.mdhooks.mdlink-options.mdREADME.mdremote-functions.mdserver-only.mdservice-worker.mdshallow-snapshots.mdstate-management.md详细介绍
SvelteKit 进阶
SvelteKit 进阶功能参考。涵盖服务器端与客户端之间的状态管理、远程函数 API、环境变量(旧版和显式配置)、钩子(服务器端/共享/通用)、错误处理、链接选项、服务工作线程、仅限服务器端的模块、快照、浅层路由,以及 $app/* 模块。
来源:SvelteKit 官方 llms.txt 文档。
何时使用此技能
在以下情况下使用此技能:
- 选择状态的存放位置(服务器上下文、URL、快照、存储)。
- 使用
query/form/command/prerender构建类型安全的客户端/服务器 RPC。 - 安全读取环境变量(私有与公开、静态与动态)。
- 通过钩子(
handle、handleFetch、handleError、handleValidationError、reroute、transport、init)自定义请求处理。 - 抛出预期/非预期错误、自定义备用错误页面,并通过
App.Error定义错误结构的类型。 - 使用
data-sveltekit-*属性配置链接行为。 - 添加服务工作线程,以支持离线使用/预缓存。
- 通过
.server.*或$lib/server/防止机密泄露。 - 使用快照持久化临时 DOM 状态,或通过浅层路由持久化路由历史条目。
- 使用
$app/*模块(forms、navigation、state、paths、server、environment、types)。
不要将此技能用于路由、表单操作(旧版)、加载、基础设置、环境变量之外的适配器配置,或 CSS/样式设置。
关键部分
1. 状态管理
- 不要共享服务器状态 — 模块级变量(
let user;)会在请求之间泄露。通过 Cookie 进行身份验证,并将数据持久化到数据库。 - 保持
load函数纯粹 — 不要产生副作用(例如使用全局存储)。应改为返回数据。 - 使用上下文保存每个请求的状态 —
setContext('user', () => data.user)(传入函数,使响应性能够跨越边界)。在 SSR 期间于子组件中读取上下文时,更新不会传递到父组件(父组件已完成渲染)。 - 组件状态会在导航过程中保留 — 从
data派生的const只会计算一次。对于应在数据变化时重新计算的值,请使用$derived(...)。 - URL 状态 — 使用搜索参数保存筛选条件/排序方式;重新加载后仍会保留,并影响 SSR。
- 快照 — 用于可丢弃的 UI 状态(例如“手风琴面板是否展开?”),并与历史记录条目绑定。
2. 远程函数(自 2.27 起,实验性功能)
在 svelte.config.js 中启用:
kit: { experimental: { remoteFunctions: true } },
compilerOptions: { experimental: { async: true } }
从 *.remote.js 导出的类型:
| 类型 | 用途 | 主要功能 |
|---|---|---|
query | 读取动态服务器数据 | 去重、refresh()、loading/error/current |
query.batch | 在一次调用中批量执行 n+1 个查询 | 返回 (input, idx) => Output |
query.live | 实时异步可迭代对象 | connected、reconnect();第一个值会序列化以用于 SSR |
form | 渐进增强的 <form> | <form {...createPost}>、createPost.fields.x.as('text')、validate()、enhance()、for(id)、preflight(schema) |
command | 在事件处理器中执行命令式变更 | 渲染期间不可调用 |
prerender | 构建时数据 | inputs、dynamic: true |
- 使用任何 Standard Schema(Zod/Valibot)进行验证。
- 参数/返回值通过 devalue 序列化。
- 在远程函数中可通过
getRequestEvent()使用 Cookie。 query/form/prerender中允许使用redirect(...),command中不允许。- 单航班变更:在服务器处理器中使用
getPosts().refresh()或getPost(id).set(...);客户端请求的刷新可使用requested(getPosts, 1).refreshAll()(限制用于防范 DoS)。
3. 环境变量
旧版 $env/*(SvelteKit 2.63 之前的默认方式):
| 模块 | 作用范围 | 时机 |
|---|---|---|
$env/dynamic/private | 仅限服务端 | 运行时(类似 process.env) |
$env/dynamic/public | 公开(PUBLIC_*) | 运行时 |
$env/static/private | 仅限服务端 | 构建时(内联) |
$env/static/public | 公开 | 构建时(内联) |
显式环境变量(在 v3 中选择启用,且为默认方式):
在 svelte.config.js 中启用:kit.experimental.explicitEnvironmentVariables = true。然后创建 src/env.ts:
import { defineEnvVars } from '@sveltejs/kit/hooks';
import * as v from 'valibot';
import { building } from '$app/env';
export const variables = defineEnvVars({
API_KEY: {}, // private
GOOGLE_ANALYTICS_ID: { public: true }, // public
SHOW_DEBUG_OVERLAY: { public: true, static: true }, // inlined, dead-code-eliminated
SECRET: { schema: building ? v.optional(v.string()) : v.string() },
CACHE_TTL_SECONDS: { description: 'How long...' }
});
从 $app/env/private 或 $app/env/public 导入。$app/environment 已重命名为 $app/env。
4. 钩子
三个可选文件:src/hooks.server.js、src/hooks.client.js、src/hooks.js。
服务端(hooks.server.js):
handle({ event, resolve })— 在每次请求时运行;返回 Response,或调用resolve(event, opts)。resolve的选项:transformPageChunk、filterSerializedResponseHeaders、preload。有多个时使用sequence(...)。handleFetch({ request, fetch, event })— 将跨源请求重写为内部 API 请求;为同级子域转发 cookie。handleValidationError({ event, issues })— 为有误的远程函数参数自定义 400 响应。
共享(hooks.server.js 和 hooks.client.js):
handleError({ error, event, status, message })— 仅用于意外错误。返回{ message, ... }-> 成为page.error。绝不能抛出异常。服务端类型:HandleServerError;客户端类型:HandleClientError;客户端的event是NavigationEvent。init()— 在启动时运行一次;适用于建立数据库连接。
通用(hooks.js):
reroute({ url, fetch })— 将 URL 转换为不同的路由(例如,i18n)。必须是纯函数且幂等。自 2.18 起可以是异步函数。transport— 为跨越服务端/客户端边界的类型(例如,Vector)自定义编码器/解码器。
5. 错误
- 预期错误 — 从
@sveltejs/kit调用error(404, 'Not found')(或error(404, { message, code }))。渲染最近的+error.svelte,并设置状态码。page.error= 传入的对象。 - 意外错误 — 任何其他异常。不会对外公开(显示通用的
Internal Error);通过handleError路由处理。 - 渲染错误 — 通过
experimental.handleRenderingErrors选择启用。错误会直接作为error属性传递给+error.svelte(而非通过page.error)。 - 响应 — 使用自定义
src/error.html,其中包含%sveltekit.status%和%sveltekit.error.message%。根目录+layout.server.js中的错误会使用回退页面(根目录包含+error.svelte)。 - 类型安全 — 在
src/app.d.ts中声明App.Error接口:
declare global {
namespace App {
interface Error { message: string; code: string; id: string; }
}
}
6. 链接选项
在 <a>(或其父元素)上使用 data-sveltekit-* 属性。也可用于 <form method="GET">。
| 属性 | 值 | 效果 |
|---|---|---|
preload-data | hover(默认)、tap | 在悬停/轻触时预加载 load 数据 |
preload-code | eager、viewport、hover、tap | 仅预加载路由代码 |
reload | 布尔值 | 强制整页导航(rel="external" 也会如此) |
replacestate | 布尔值 | 替换历史记录条目,而非推入新条目 |
keepfocus | 布尔值 | 导航后保持焦点在同一元素上 |
noscroll | 布尔值 | 禁用导航后滚动到顶部 |
在子树中使用 data-sveltekit-preload-data="false" 可禁用预加载。遵循 navigator.connection.saveData 的设置。
7. Service Worker
放置 src/service-worker.js(或 src/service-worker/index.js)——会自动打包并注册。通过配置禁用后,可手动注册。
可从 $service-worker 获取:base、build、files、prerendered、version。
标准做法:在安装时缓存 build + files,获取时优先使用网络,缓存作为回退。跳过带有 Cache-Control: no-store 的响应(实时查询)。在开发环境中,build/prerendered 是空数组。
8. 仅限服务端的模块
$env/static/private和$env/dynamic/private— 仅限服务端。$app/server— 仅限服务端。- 你的模块 — 添加
.server.js后缀,或放在$lib/server/下。
从浏览器代码中非法导入会导致构建报错。对于仅用于类型的导入,请使用 import type。在测试中会禁用检测(process.env.TEST === 'true')。
9. 快照与浅层路由
快照 — 从 +page.svelte 或 +layout.svelte 导出 snapshot = { capture, restore }。页面更新前会将快照保存到 sessionStorage;浏览历史导航时会恢复。内容必须可序列化为 JSON。不要返回过大的对象。
浅层路由 — pushState(url, state) / replaceState(url, state) 会创建历史记录条目,但不会进行导航。通过 page.state 读取。使用 preloadData(href) 获取 load 数据,然后使用 pushState(href, { selected: result.data }),即可在模态框中渲染另一个 +page.svelte。首次 SSR 和首次绘制期间,page.state 始终为 {}。
快速修复
| 症状 | 修复方法 |
|---|---|
| 用户数据在请求之间泄漏 | 不要使用模块级变量;使用 Cookie + DB |
导航时 load 数据未更新 | 对从 data 派生的值使用 $derived(...),而不是 const |
Cannot import $lib/server/... | 将共享类型移到 import type |
| 实时查询流停止 | 使用 .reconnect() 重新连接;不要在 SW 中缓存 no-store 响应 |
error(404, ...) 显示空白页 | 在离路由最近的位置添加 +error.svelte |
| 仅限服务端的环境变量泄漏到客户端 | 使用 $env/static/private 或 .server 命名 |
| 验证失败时显示通用的 400 消息 | 实现 handleValidationError |
| 导航时组件状态丢失 | 使用 {#key page.url.pathname}<X/>{/key} 包裹,以强制重新挂载 |
注意事项
load必须是纯函数 — 不要写入全局 store。应改为返回数据。- SSR 期间的上下文更新不会向上传播 — 向下传递状态,避免水合时出现闪烁。
query.batch会返回单个函数,用于将各个输入映射到输出;它不会直接返回结果数组。- SSR 上的
query.live只返回第一个生成的值,然后关闭。 - 渲染期间不能调用
command— 应从事件处理器中调用。 requested需要limit— 用于防止 DoS;只有在明确安全时才传入Infinity。- SvelteKit 2.x 中,
error()不再需要throw。 - SSR 和首次绘制时,
page.state为{}— 不要依赖它进行关键渲染。 reroute必须是纯函数且幂等 — 其结果会按 URL 在客户端缓存。- 开发模式下,Service Worker 的
build/prerendered为空 — 请在生产构建中测试。 - 向同级子域名转发 Cookie 需要手动实现
handleFetch— SvelteKit 无法判断哪个父域 Cookie 属于哪个子域名。 handleError不得抛出异常 — 使用 try/catch 包裹有风险的操作。
常见问题
问:$app/state 和 $app/stores 有什么区别?
答:$app/state(自 2.12 起)基于 rune,具有响应性。$app/stores 是基于 store 的旧版本。使用 Svelte 5 时,请使用 $app/state。
问:query 和 prerender 有什么区别?
答:query 用于动态数据;prerender 用于构建时冻结的数据,这些数据可以从 CDN 提供。完全预渲染的页面不能使用 query。
问:form 和 command 有什么区别?
答:form 支持渐进增强——无需 JS 即可使用,并可应用于 <form>。command 仅限 JS,通过事件处理程序调用。优先使用 form。
问:$env/dynamic 和 $env/static 有什么区别?
答:dynamic 在运行时读取(例如 process.env);static 在构建时内联(可启用死代码消除)。对于构建时已知的值,请使用 static。
问:$env/static/private 和 $env/dynamic/private 有什么区别?
答:两者的访问限制相同;区别在于读取值的时机。Static 支持 DCE;dynamic 允许在运行时覆盖(例如 MY_FLAG=1 npm run dev)。
问:可以在 +page.svelte 中使用 $lib/server 模块吗? 答:不可以——仅限服务器端的模块不能被客户端代码直接或间接导入。SvelteKit 会在构建时报错。
问:提交后如何让焦点保持在搜索输入框中?
答:将 data-sveltekit-keepfocus 添加到 <form>。
问:如何在 load 中抛出 404?
答:import { error } from '@sveltejs/kit'; error(404, 'Not found');(在 SvelteKit 2 中不要使用 throw)。
问:应该使用 Sentry 吗?
答:应该——在 hooks.server.js 和 hooks.client.js 的 handleError 中进行初始化。服务器端使用 HandleServerError;客户端使用带有 NavigationEvent 的 HandleClientError。
示例
| 主题 | 文件 |
|---|---|
| 使用 context + URL + derived 管理状态 | examples/state-management.md |
| Remote query/form/command/batch/live | examples/remote-functions.md |
| 环境变量(旧版 + 显式配置) | examples/env-vars.md |
| 服务器端 + 通用 hooks | examples/hooks.md |
| 预期/意外错误 + App.Error | examples/errors.md |
| data-sveltekit-* 链接选项 | examples/link-options.md |
| Service worker 预缓存 + 离线 | examples/service-worker.md |
| $app/* 模块(表单、导航、状态、路径、服务器) | examples/app-modules.md |
| Snapshots + 浅层路由 | examples/shallow-snapshots.md |
参考资料
| 主题 | 文件 |
|---|---|
| 状态管理 | references/state-management.md |
| Remote 函数 | references/remote-functions.md |
| 环境变量 | references/env-vars.md |
| Hooks | references/hooks.md |
| 错误 | references/errors.md |
| 链接选项 | references/link-options.md |
| Service worker | references/service-worker.md |
| 仅限服务器端的模块 | references/server-only.md |
| $app/* 模块 | references/app-modules.md |
| Snapshots 和浅层路由 | references/shallow-snapshots.md |