ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

SvelteKit 2 升级迁移完全指南:从 v1 到 v2 的全部破坏性变更与实战改造

2026/9/21 15:05:43 拓冰建站 浏览量
SvelteKit 2 升级迁移完全指南:从 v1 到 v2 的全部破坏性变更与实战改造 SvelteKit 2 升级迁移完全指南从 v1 到 v2 的全部破坏性变更与实战改造【免费下载链接】kitweb development, streamlined项目地址: https://gitcode.com/gh_mirrors/kit/kitSvelteKit 2.0 在保持 1.x 开发体验的基础上修正了大量历史遗留的不一致行为如顶层 Promise 的隐式 await、resolvePath缺少base支持、cookie path 歧义等引入更严格的错误处理与更安全的默认值。本文以官方迁移文档 documentation/docs/60-appendix/30-migrating-to-sveltekit-2.md 为骨架结合本仓库packages/kit源码逐一拆解全部破坏性变更并给出可直接套用的迁移代码示例与自动迁移命令帮助你平滑、无遗漏地完成升级。从 SvelteKit 1 升级到 2 大多是无痛的破坏性变更相对集中。官方推荐两条前置路径先升级到最新的 1.x 版本利用定向弃用警告提前发现待改点并先升级到 Svelte 4较新的 1.x 已支持它而 SvelteKit 2.0 强制要求它。大部分机械化改动可通过npx sv migrate sveltekit-2自动完成。一、迁移准备自动迁移命令与前置依赖迁移第一步是运行自动迁移工具。SvelteKit 2 起官方迁移命令统一收敛到svSvelte CLInpx sv migrate sveltekit-2该命令会自动处理本文后续提到的大部分代码替换error/redirect去throw、cookie 的path占位注释、resolvePath到resolveRoute的替换、package.json依赖版本更新、tsconfig.json中冗余 TypeScript 标志清理等。不过它只会高亮需要调整的位置如 cookie path或替换方法本身如resolveRoute涉及业务语义的调整如手动拼接base仍需人工确认。在跑迁移之前请确认依赖满足 2.0 的最低要求详见下文依赖要求升级一节。文档明确强调先升 1.x 最新版再升 2.0这样 1.x 的弃用警告能提前标出所有将来会破坏的用法。二、error与redirect不再需要手动throwv1 中你必须把error(...)、redirect(...)的返回值手动throw出去import { error } from sveltejs/kit; // 旧写法v1 throw error(500, something went wrong); // 新写法v2 error(500, something went wrong);v2 中直接调用函数即可SvelteKit 内部会负责中断流程。svelte-migrate会自动完成这类替换。在try {...}块中区分框架异常文档特别提示不要这样做即不要用 try/catch 包住 error/redirect但如果确实存在这种代码可以用类型守卫函数区分框架抛出的异常与意外错误import { isHttpError, isRedirect } from sveltejs/kit; try { // ... } catch (e) { if (isHttpError(e, 404)) { // 这是 error(404, ...) 触发的预期错误 } else if (isRedirect(e)) { // 这是 redirect(...) 触发的跳转 } else { // 真正的意外异常 } }其实现位于 packages/kit/src/exports/index.jsisHttpError(e, status)检查e instanceof HttpError可选的status参数可进一步过滤具体状态码isRedirect(e)检查e instanceof Redirect。二者都以类型谓词type predicate形式返回可同时帮助 TypeScript 收窄类型。三、设置 Cookie 必须显式指定path当Set-Cookie头未指定path时浏览器会按 RFC 6265 §5.1.4 把 cookie path 设为当前资源所在路径的父目录这极易导致开发者以为 cookie 作用于整个域名、实际却只在部分路径生效的隐性 bug。因此v2 起cookies.set、cookies.delete、cookies.serialize都必须传path/** type {import(./$types).PageServerLoad} */ export function load({ cookies }) { cookies.set(name, value, { path: / }); // 必须显式提供 path return { response }; }绝大多数场景用path: /即可也支持相对路径表示当前路径.表示当前目录。svelte-migrate会在需要调整的位置添加高亮注释。从源码看服务端 cookie 封装在 packages/kit/src/runtime/server/cookie.js 中默认值对象里path: /只是兜底默认同时该模块在 dev 模式下会按domain/path?name的键跟踪所有已设置 cookie 的 path用于检测疑似错误 path 用法并发出警告匹配 cookie 时也以 path 长度取最长匹配。显式指定 path 正是为了让这一套匹配与警告机制语义清晰。四、顶层 Promise 不再自动 awaitv1 中load函数返回对象里的顶层属性若是 Promise 会被自动 await引入流式渲染streaming后这一行为变得尴尬——它迫使流式数据被多嵌套一层。v2 起 SvelteKit 不再区分顶层与非顶层 Promise所有 Promise 都保持原样返回便于流式传输。若想恢复阻塞式行为请显式使用await多路并行时配合Promise.all防止瀑布请求// 单个 Promise加 async await /** type {import(./$types).PageServerLoad} */ export async function load({ fetch }) { const response await fetch(url).then(r r.json()); return { response }; }// 多个 Promise用 Promise.all 并行等待 /** type {import(./$types).PageServerLoad} */ export async function load({ fetch }) { const [a, b] await Promise.all([ fetch(url1).then(r r.json()), fetch(url2).then(r r.json()), ]); return { a, b }; }迁移时注意原本依赖顶层自动 await来阻塞渲染的代码改为显式 await 后行为不变而需要流式的数据v2 中直接返回 Promise 即可无需再人为嵌套一层。五、goto(...)行为收紧goto(...)不再接受外部 URL。需要跳转到站外地址时请直接使用window.location.href url;同时goto传入的state对象现在会决定$page.state并且若声明了App.PageState接口必须符合该接口。这与浅路由shallow routing机制相关详见 documentation/docs/30-advanced/67-shallow-routing.md 与 documentation/docs/20-core-concepts/50-state-management.md。六、路径默认相对化paths.relative语义统一v1 中app.html里的%sveltekit.assets%在 SSR 时默认被替换为相对路径如.、..、../..取决于当前渲染路径的深度除非显式设置paths.relative: false而$app/paths导出的base、assets则只有在paths.relative显式为true时才相对化——两套行为不一致。v2 修正了这一矛盾路径要么总是相对要么总是绝对完全取决于paths.relative配置项且默认值为true。默认相对化能显著提升应用可移植性当base与实际部署位置不符如被 Internet Archive 中paths一节含base、assets、relative三个子项。七、服务端fetch不再可追踪v1 曾支持追踪服务端fetch的 URL 以决定是否重跑 load 函数但这会带来私有 URL 泄露的安全风险因此该能力一直藏在dangerZone.trackServerFetches开关后面。v2 中该开关已被移除服务端 fetch 不再可追踪——如果你曾依赖追踪服务端 fetch 以触发 load 重跑的行为需要改用其他显式失效机制如invalidate/invalidateAll参见 documentation/docs/20-core-concepts/20-load.md。八、preloadCode参数必须带base前缀SvelteKit 暴露了两个编程式预加载函数preloadCode与preloadData用于按需加载某路径对应的代码与数据。v1 有个隐蔽的不一致传给preloadCode的路径不需要加base前缀而preloadData需要。v2 统一了两者——若配置了base两个函数的参数都必须以base开头import { base } from $app/paths; import { preloadCode, preloadData } from $app/navigation; preloadCode(${base}/foo); // v2必须拼上 base preloadData(${base}/foo);另外preloadCode的参数从可变数量的 n 个参数改为只接受单个参数。九、resolvePath移除改用resolveRoutev1 的resolvePath(routeId, params)能把路由 ID如/blog/[slug]与参数如{ slug: hello }解析成 pathname但返回值不包含base前缀在配置了base的场景下用处受限。v2 用命名更贴切的resolveRoute取代它并自动把base纳入计算// 旧写法v1 import { resolvePath } from sveltejs/kit; import { base } from $app/paths; const path base resolvePath(/blog/[slug], { slug }); // 新写法v2 import { resolveRoute } from $app/paths; const path resolveRoute(/blog/[slug], { slug });svelte-migrate会自动完成方法替换但如果你的旧代码里还有手动拼接base resolvePath(...)的部分需要自行删掉那个base 。其底层实现位于 packages/kit/src/utils/routing.jsresolve_route(id, params)先通过get_route_segments切分路由段自动剔除(group)分组段再用segment_pattern正则逐个替换[param]、[[optional]]、[...rest]与转义序列缺失必填参数、参数值以斜杠开头/结尾或类型非法时都会抛出明确的错误信息返回值会保留原路由 ID 的尾斜杠/blog/[slug]/解析后同样带尾斜杠。完整导出与类型见 documentation/docs/98-reference/20-$app-paths.md。十、错误处理改进handleError新增status与messagev1 的错误处理不一致部分错误会触发handleError钩子却难以分辨状态码例如判断 404 与 500 只能靠event.route.id是否为null另一些错误如对无 action 的页面发POST产生的 405根本不会触发handleError导致$page.error偏离已声明的App.Error类型。v2 统一了这一行为所有错误都会调用handleError钩子并携带两个新属性status与message。对于你自己代码或其调用的库代码抛出的错误status为500message为Internal Error。要点error.message可能包含不该暴露给用户的敏感信息而message是安全的可放心用于日志与展示。配套的仓库实现可见 packages/kit/src/utils/error.jsget_status(error)对HttpError与SvelteKitError返回其status其余一律视为500add_deprecated_handle_error_properties则在 dev 模式下为旧式顶层status/message访问提供兼容 getter 并输出弃用警告提示改用error.status/error.message。十一、预渲染期间禁止读取动态环境变量$env/dynamic/public与$env/dynamic/private提供运行时环境变量而$env/static/public与$env/static/private提供构建时环境变量。v1 中两者在预渲染时混为一谈预渲染页面里读取的动态变量其实被烘焙成了构建时的值这是错误的更糟的是用户先落在预渲染页再导航到动态渲染页时浏览器里的$env/dynamic/public会被这些过期值污染。v2 的修正是预渲染期间不再允许读取动态环境变量应改用static模块。若用户落在预渲染页面SvelteKit 会从服务器请求$env/dynamic/public的最新值默认来自/_app/env.js模块而不是从服务端渲染的 HTML 里读取。相关用法详见 documentation/docs/20-core-concepts/70-environment-variables.md 及参考文档 documentation/docs/98-reference/15-sveltejs-kit-env.md。十二、use:enhance回调移除form与data给use:enhance传入回调时回调会收到一个包含多种有用属性的对象。v1 中该对象包含form与data这两者早已被弃用并建议改用formElement与formDatav2 中form与data被彻底移除。迁移时把回调参数里的form改成formElement、data改成formData即可。十三、含文件输入的表单必须用multipart/form-data如果表单里有input typefile却没有enctypemultipart/form-data在无 JS 提交时文件会被丢弃。为确保无 JS 场景下表单行为正确v2 会在use:enhance提交遇到此类表单时直接抛错把问题暴露在开发期而不是静默丢文件。迁移检查项所有含文件输入的表单都补上enctypemultipart/form-data。十四、生成的tsconfig.json校验更严格v1 中当你的tsconfig.json包含paths或baseUrl时SvelteKit 会尽力拼出一个还算有效的配置。v2 的校验更严格在tsconfig.json中使用paths或baseUrl会触发警告。因为这些设置被用来生成路径别名官方建议改用 SvelteKit 插件Vite 插件的alias配置项这样还能同时为打包器创建对应的别名避免 TS 与 Vite 两套别名不一致。例如在vite.config.js中import { sveltekit } from sveltejs/kit/vite; import { defineConfig } from vite; export default defineConfig({ plugins: [sveltekit()], kit: { alias: { components: ./src/lib/components } } });十五、getRequest不再立即抛错sveltejs/kit/node模块提供面向 Node 环境的辅助函数其中getRequest把 Node 的ClientRequest转成标准Request对象。v1 中若Content-Length头超过指定大小限制getRequest会直接抛错v2 把错误延迟到读取请求体如有时才抛出从而带来更好的诊断信息与更简洁的调用代码。依赖getRequest的 Node 适配层代码在升级后无需特殊改动但错误捕获位置可能从创建 Request 时移到消费 body 时相关 try/catch 需要相应调整。十六、vitePreprocess不再从sveltejs/kit/vite导出由于sveltejs/vite-plugin-svelte现在是 SvelteKit 的peer dependencyv2 不再从sveltejs/kit/vite转发导出vitePreprocess请直接改从源头导入// 旧写法v1 import { vitePreprocess } from sveltejs/kit/vite; // 新写法v2 import { vitePreprocess } from sveltejs/vite-plugin-svelte;十七、依赖要求升级一览SvelteKit 2 要求Node18.13或更高以及下列最低依赖版本svelte-migrate会自动更新你的package.json包最低版本备注svelte4必须vite5必须typescript5必须sveltejs/vite-plugin-svelte3现为 SvelteKit 的peerDependency此前是直接依赖sveltejs/adapter-cloudflare3如使用对应适配器sveltejs/adapter-cloudflare-workers2如使用对应适配器sveltejs/adapter-netlify3如使用对应适配器sveltejs/adapter-node2如使用对应适配器sveltejs/adapter-static3如使用对应适配器sveltejs/adapter-vercel4如使用对应适配器随 TypeScript 升级生成的tsconfig.json即你的tsconfig.json所 extends 的那份现在使用moduleResolution: bundler——TypeScript 官方推荐能正确解析带exportsmap 的包类型verbatimModuleSyntax——取代原有的importsNotUsedAsValues与preserveValueImports标志若你的tsconfig.json里还留着这两个标志请删除svelte-migrate会自动处理。十八、SvelteKit 2.12$app/stores弃用迁移到$app/stateSvelteKit 2.12 引入基于 Svelte 5 runes API 的$app/state导出page、navigating、updated见 packages/kit/src/runtime/app/state/index.js。它提供$app/stores的全部能力且使用位置与方式更灵活最重要的是page对象变为细粒度响应式——例如更新page.state不会使page.data失效反之亦然。因此$app/stores已被弃用并将在 SvelteKit 3 中移除。官方建议若尚未升级先升级到 Svelte 5再把$app/stores迁移到$app/state。绝大多数替换很简单把 import 来源从$app/stores换成$app/state并去掉使用处的$前缀script // 旧写法v1 / v2.12 之前 import { page } from $app/stores; /script {$page.data} script // 新写法v2.12 import { page } from $app/state; /script {page.data}对.svelte组件内大部分$app/stores用法可运行npx sv migrate app-state自动迁移。完整 API 见 documentation/docs/98-reference/20-$app-state.md旧模块说明见 documentation/docs/99-legacy-reference/20-$app-stores.md。结语一份完整的升级检查清单综合全文从 v1 升级到 v2 时建议按以下清单逐项核对先升到 1.x 最新版与 Svelte 4再跑npx sv migrate sveltekit-2删除所有手动throw error(...)/throw redirect(...)中的throw为所有cookies.set/cookies.delete/cookies.serialize补上path检查load返回的顶层 Promise 是否符合预期需要阻塞就显式await/Promise.all确认goto不再跳外部 URL、state符合App.PageState接受路径默认相对化或按需显式配置paths.relative移除对dangerZone.trackServerFetches的依赖preloadCode/preloadData参数补base前缀把resolvePath换成resolveRoute并删掉手动base 审视handleError是否用到新的status/message并避免把error.message暴露给用户预渲染相关代码改用$env/static/*use:enhance回调里的form→formElement、data→formData含input typefile的表单补enctypemultipart/form-data从tsconfig.json移除paths/baseUrl改用 Vite 插件alias配置从sveltejs/vite-plugin-svelte导入vitePreprocess按上表升级 Node 与全部依赖版本、清理废弃 TS 标志v2.12逐步把$app/stores迁移到$app/state。完成以上各项后你的应用即可稳定运行在 SvelteKit 2 之上并享受更统一、更安全、更可移植的框架行为。【免费下载链接】kitweb development, streamlined项目地址: https://gitcode.com/gh_mirrors/kit/kit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考