升级从 SvelteKit 版本 1 到版本 2 应该大部分是无缝的。有一些需要注意的重大更改,列在这里。您可以使用 `npx sv migrate sveltekit-2` 自动迁移其中的一些更改。
强烈建议在升级到 2.0 版本之前先升级到最新的 1.x 版本,以便您能够利用有针对性的弃用警告。我们还建议首先[更新到 Svelte 4](../svelte/v4-migration-guide):SvelteKit 1.x 的后续版本支持它,而 SvelteKit 2.0 则要求必须使用它。
##
`redirect` 和 `error` 已不再由您抛出
之前,您必须自己 `抛出` 由 `error(...)` 和 `redirect(...)` 返回的值。在 SvelteKit 2 中,这种情况不再存在——调用函数就足够了。
```js
import { error } from '@sveltejs/kit'
// ...
---throw error(500, 'something went wrong');---
+++error(500, 'something went wrong');+++
```
`سوفت-مهاجرت`将为您自动执行这些更改。
如果错误或重定向在`try {...}`块内部抛出(提示:不要这样做!),您可以使用从`@sveltejs/kit`导入的`isHttpError`和`isRedirect`来区分它们与意外错误。
##
路径设置 cookie 时是必需的
当接收到未指定 `path` 的 `Set-Cookie` 标头时,浏览器会将 cookie 路径设置为所涉及资源的父路径。这种行为并不特别有用或直观,并且经常导致错误,因为开发者原本期望 cookie 应用于整个域名。
截至 SvelteKit 2.0,调用`cookies.set(...)`、`cookies.delete(...)`或`cookies.serialize(...)`时,您需要设置一个`path`,以便没有歧义。大多数情况下,您可能想使用`path: '/'`,但您可以将其设置为任何您喜欢的路径,包括相对路径——`''`表示'当前路径',`'.'`表示'当前目录'。
```js
/** @type {import('./$types').PageServerLoad} */
export function load({ cookies }) {
cookies.set(name, value, +++{ path: '/' }+++);
return { response }
}
```
`سلتو-مigrate` 将添加注释以突出需要调整的位置。
##
顶级承诺不再等待
在 SvelteKit 版本 1 中,如果从`load`函数返回的对象的顶级属性是 promises,它们会自动等待。随着[流式传输的引入,这种行为变得有些尴尬,因为它迫使你将流式数据嵌套在一层之内。](/blog/streaming-snapshots-sveltekit)
截至版本 2,SvelteKit 不再区分顶层和非顶层承诺。要恢复阻塞行为,请使用`await`(在适当的情况下,使用`Promise.all`以防止瀑布效果):
```js
// @filename: ambient.d.ts
declare const url: string;
// @filename: index.js
// ---cut---
// If you have a single promise
/** @type {import('./$types').PageServerLoad} */
export +++async+++ function load({ fetch }) {
const response = +++await+++ fetch(url).then(r => r.json());
return { response }
}
```
```js
// @filename: ambient.d.ts
declare const url1: string;
declare const url2: string;
// @filename: index.js
// ---cut---
// If you have multiple promises
/** @type {import('./$types').PageServerLoad} */
export +++async+++ function load({ fetch }) {
--- const a = fetch(url1).then(r => r.json());---
--- const b = fetch(url2).then(r => r.json());---
+++ const [a, b] = await Promise.all([
fetch(url1).then(r => r.json()),
fetch(url2).then(r => r.json()),
]);+++
return { a, b };
}
```
##
goto(...) 变更
`goto(...)` 不再接受外部 URL。要导航到外部 URL,请使用 `window.location.href = url`。现在 `state` 对象决定 `$page.state`,并且如果已声明,必须遵循 `App.PageState` 接口。有关更多详细信息,请参阅 [浅路由](shallow-routing)。
##
路径现在默认为相对路径
在 SvelteKit 1 中,您的`app.html`中的`%sveltekit.assets%`在服务器端渲染时默认被替换为相对路径(即`.`或`..`或`../..`等,具体取决于渲染的路径),除非显式地将`paths.relative`配置选项设置为`false`。同样,从`$app/paths`导入的`base`和`assets`也是如此,但仅当显式将`paths.relative`选项设置为`true`时。
此不一致性已在版本 2 中修复。路径要么始终是相对的,要么始终是绝对的,具体取决于[`paths.relative`](configuration#paths)的值。默认为`true`,因为这会产生更便携的应用程序:如果`base`不是应用程序期望的(例如在[互联网档案馆](https://archive.org/)查看时)或构建时未知(例如部署到[IPFS](https://ipfs.tech/)等),则出现问题的可能性更小。
##
服务器抓取不再可追踪
之前可以从服务器上的`fetch`es 跟踪 URL 以重新运行加载函数。这可能导致潜在的安全风险(私人 URL 泄露),因此它位于`dangerZone.trackServerFetches`设置之后,该设置现已移除。
##
`preloadCode` 参数必须以 `base` 前缀
SvelteKit 公开了两个函数,[`preloadCode`]($app-navigation#preloadCode) 和 [`preloadData`]($app-navigation#preloadData),用于以编程方式加载与特定路径关联的代码和数据。在版本 1 中,存在一个微妙的不一致——传递给`preloadCode`的路径不需要(如果已设置)以`base`路径作为前缀,而传递给`preloadData`的路径则需要。
这是在 SvelteKit 2 中修复的——在两种情况下,如果已设置,路径应该以`base`为前缀。
此外,`preloadCode` 现在接受单个参数,而不是 *n* 个参数。
##
`resolvePath` 已被移除
SvelteKit 1 包含了一个名为 `resolvePath` 的函数,该函数允许您将路由 ID(如 `/blog/[slug]`)和一组参数(如 `{ slug: 'hello' }`)解析为路径名。不幸的是,返回值没有包括 `base` 路径,限制了其在 `base` 已设置的情况下的实用性。
因此,SvelteKit 2 将 `resolvePath` 替换为(名称稍好一些的)函数 `resolveRoute`,该函数从 `$app/paths` 导入,并考虑了 `base`。
```js
---import { resolvePath } from '@sveltejs/kit';
import { base } from '$app/paths';---
+++import { resolveRoute } from '$app/paths';+++
---const path = base + resolvePath('/blog/[slug]', { slug });---
+++const path = resolveRoute('/blog/[slug]', { slug });+++
```
`سلتو-مigrate` 将为您执行方法替换,尽管如果您稍后用 `base` 预先添加结果,您需要自己将其删除。
##
改进的错误处理
错误在 SvelteKit 1 中处理不一致。一些错误会触发`handleError`钩子,但没有好的方法来辨别它们的状态(例如,区分 404 和 500 的唯一方法是通过查看`event.route.id`是否为`null`),而其他错误(如对没有操作的页面进行`POST`请求的 405 错误)根本不会触发`handleError`,但应该会。在后一种情况下,结果`$page.error`将与指定的`App.Error`[(](types#Error)如果指定的话[)](types#Error)类型不符。
SvelteKit 2 通过调用 `handleError` 钩子并使用两个新属性:`status` 和 `message` 来清理。对于从您的代码(或由您的代码调用的库代码)抛出的错误,状态将是 `500`,消息将是 `内部错误`。而 `error.message` 可能包含不应向用户暴露的敏感信息,`message` 则是安全的。
##
动态环境变量在预渲染期间无法使用
The `$env/dynamic/public` 和 `$env/dynamic/private` 模块提供对 *运行时* 环境变量的访问,与 `$env/static/public` 和 `$env/static/private` 提供的 *构建时* 环境变量相反。
在 SvelteKit 1 的预渲染期间,它们是相同的。因此,使用 '动态' 环境变量的预渲染页面实际上是在“烘焙”构建时间值,这是不正确的。更糟糕的是,如果用户在导航到动态渲染的页面之前恰好访问了预渲染的页面,`$env/dynamic/public` 将在浏览器中使用这些过时的值进行填充。
由于这个原因,在 SvelteKit 2 的预渲染过程中无法读取动态环境变量 — 您应该使用 `静态` 模块。如果用户访问预渲染的页面,SvelteKit 将从服务器(默认情况下从名为`/_app/env.js`的模块)请求最新的`$env/dynamic/public`值,而不是从服务器渲染的 HTML 中读取它们。
##
`表单` 和 `数据` 已从 `使用:增强` 回调中移除
如果您提供了一个回调到[`使用:增强`](form-actions#Progressive-enhancement-use:enhance),它将用一个包含各种有用属性的对象被调用。
在 SvelteKit 1 中,这些属性包括`form`和`data`。这些属性在一段时间前已被弃用,转而使用`formElement`和`formData`,并在 SvelteKit 2 中完全删除。
##
表单中包含文件输入时必须使用 `multipart/form-data`
如果一个表单包含一个``但没有任何`enctype="multipart/form-data"`属性,非 JS 提交将省略文件。SvelteKit 2 在遇到这样的表单进行`use:enhance`提交时将抛出错误,以确保在没有 JavaScript 的情况下您的表单能够正确工作。
##
生成的`tsconfig.json`更严格
之前,生成的`tsconfig.json`在您的`tsconfig.json`包含`paths`或`baseUrl`时,仍在尽力生成一个相对有效的配置。在 SvelteKit 2 中,验证更加严格,当您在`tsconfig.json`中使用`paths`或`baseUrl`时,将会发出警告。这些设置用于生成路径别名,您应该使用`svelte.config.js`中的`alias`配置选项,以创建相应的别名,并为打包器创建相应的别名。
##
`getRequest`不再抛出错误
The `@sveltejs/kit/node` 模块导出用于 Node 环境的辅助函数,包括 `getRequest`,它将 Node [`ClientRequest`](https://nodejs.org/api/http.html#class-httpclientrequest) 转换为标准的 [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) 对象。
在 SvelteKit 1 中,`getRequest` 如果 `Content-Length` 头部超出指定大小限制时可能会抛出异常。在 SvelteKit 2 中,错误将不会立即抛出,而是在读取请求体(如果有)时才会抛出。这有助于更好地进行诊断和编写更简单的代码。
##
`vitепPгeprеss`不再从`@sveltejs/kit/vite`导出
由于 `@sveltejs/vite-plugin-svelte` 现在是一个 peer dependency,SvelteKit 2 不再导出 `vitePreprocess`。您应直接从 `@sveltejs/vite-plugin-svelte` 导入它。
##
更新依赖项要求
SvelteKit 2 需要 Node `18.13` 或更高版本,以及以下最低依赖版本:
* `斯威夫特@4`
* `vit@5`
* `typescript@5`
* `@sveltejs/vite-plugin-svelte@3` (现在作为 SvelteKit 的 `peerDependency` 是必需的 — 之前它是直接依赖的)
* `@sveltejs/adapter-cloudflare@3` (如果您使用这些适配器)
* `@sveltejs/adapter-cloudflare-workers@2`
* `@sveltejs/adapter-netlify@3`
* `@sveltejs/adapter-node@2`
* `@sveltejs/adapter-static@3`
* `@sveltejs/adapter-vercel@4`
`سلتو-مigrate` 将为您更新 `package.json`。
作为 TypeScript 升级的一部分,生成的`tsconfig.json`(你的`tsconfig.json`从中扩展而来)现在使用`"moduleResolution": "bundler"`(TypeScript 团队推荐,因为它可以正确解析具有 package.json 中`exports`映射的包中的类型)和`verbatimModuleSyntax`(它替换了现有的`importsNotUsedAsValues`和`preserveValueImports`标志——如果你在`tsconfig.json`中有这些,请删除它们。`svelte-migrate`会为你完成这项工作)。
##
SvelteKit 2.12: $app/stores 已弃用
SvelteKit 2.12 引入了基于 `$app/state` 的 [Svelte 5 runes API](/docs/svelte/what-are-runes)。`$app/state` 提供了与 `$app/stores` 相同的一切,但在使用方式和位置上提供了更大的灵活性。最重要的是,`page` 对象现在更加精细,例如,对 `page.state` 的更新不会使 `page.data` 无效,反之亦然。
因此,`$app/stores` 已弃用,并将在 SvelteKit 3 中被移除。如果您尚未升级,我们建议[升级到 Svelte 5](/docs/svelte/v5-migration-guide),然后迁移离开 `$app/stores`。大多数替代方案应该相当简单:将 `$app/stores` 的导入替换为 `$app/state`,并从使用位置删除 `$` 前缀。
```svelte
---{$page.data}---
+++{page.data}+++
```
使用`npx sv migrate app-state`自动迁移大部分在`$app/stores`中使用的`.svelte`组件。