版本 5 带来了全新的语法和反应性系统。虽然一开始可能看起来不同,但很快你就会发现许多相似之处。本指南详细介绍了这些变化,并展示了如何升级。此外,我们还提供了关于为什么进行这些更改的*原因*信息。
您不必立即迁移到新语法 - Svelte 5 仍然支持旧版 Svelte 4 语法,并且您可以使用新语法与旧语法组件混合使用,反之亦然。我们预计许多人只需更改几行代码即可升级。此外,还有一个 [迁移脚本](#Migration-script),可以帮助您自动完成许多这些步骤。
##
反应性语法更改
Svelte 5 的核心是新的 runes API。Runes 主要是编译器指令,用于告知 Svelte 关于响应性。在语法上,runes 是以美元符号开头的函数。
###
let → $状态
在 Svelte 4 中,组件顶层的一个 `let` 声明是隐式响应式的。在 Svelte 5 中,事情变得更加明确:当使用 `$state` 符文创建变量时,该变量是响应式的。让我们通过将计数器包裹在 `$state` 中将计数器迁移到符文模式:
```svelte
```
无其他更改。`count`仍然是数字本身,您可以直接读取和写入它,无需像`.value`或`getCount()`这样的包装器。
> \[!详细信息\] 为什么我们在顶层将`let`设置为隐式响应式效果很好,但这意味着响应式被限制 - 任何其他地方的`let`声明都不是响应式的。这迫使你在将代码从组件顶层重构以供重用时求助于使用存储。这意味着你必须学习一个完全不同的响应式模型,而且结果往往不那么容易处理。由于 Svelte 5 中的响应式更明确,你可以在组件顶层之外继续使用相同的 API。前往[教程](/tutorial)了解更多信息。
### $: → $derived/$effect
在 Svelte 4 中,组件顶层的一个`$:`语句可以用来声明一个派生,即完全通过其他状态的计算定义的状态。在 Svelte 5 中,这通过使用`$derived`符号实现:
```svelte
```
与`$state`一样,其他什么都没变。`double`仍然是数字本身,你可以直接读取,无需像`.value`或`getDouble()`这样的包装。
A `$:` 语句也可以用来创建副作用。在 Svelte 5 中,这通过使用 `$effect` 符文来实现:
```svelte
```
请注意,当 `$effect` 运行时与当 `$:` 运行时不同。
> \[!详细信息\] 我们为什么要这样做 `$:` 这是一个很好的简写,并且易于上手:你可以在大多数代码前加上 `$:`,它就会以某种方式工作。这种直观性也是它的缺点,因为随着代码变得越来越复杂,它就不那么容易推理了。代码的意图是创建一个导出,还是副作用?有了 `$derived` 和 `$effect`,你需要在开始时做出更多决策(剧透:90% 的时候你想要 `$derived`),但未来的你和其他团队成员将更容易处理。
>
> 也有一些难以发现的陷阱:
>
> * `$:` 仅在渲染前直接更新,这意味着您可以在重新渲染之间读取过时的值
> * `$:` 每个 tick 只运行一次,这意味着语句可能运行的频率比你想象的要低
> * `$:` 依赖项通过依赖项的静态分析确定。这在大多数情况下都有效,但在重构过程中可能会以微妙的方式出现问题,例如依赖项被移动到函数中,因此不再可见。
> * `$:` 语句也通过依赖关系的静态分析进行排序。在某些情况下可能会出现平局,导致排序错误,需要手动干预。在重构代码时,排序也可能中断,一些依赖关系因此不再可见。
>
> 最后,它对 TypeScript 不友好(我们的编辑工具必须跳过一些步骤才能使其对 TypeScript 有效),这成为了使 Svelte 的反应模型真正通用的障碍。
>
> `$derived` 和 `$effect` 修复这些问题
>
> * 总是返回最新值
> * 运行以保持稳定所需频率
> * 确定运行时的依赖,因此对重构免疫
> * 执行所需依赖项,因此不受顺序问题影响
> * TypeScript 友好
###
导出 let → $props
在 Svelte 4 中,组件属性使用 `export let` 声明。每个属性都是一个单独的声明。在 Svelte 5 中,所有属性都通过 `$props` 运行符声明,通过解构:
```svelte
```
存在多种情况,声明属性比几个`export let`声明要简单直接
* 您想重命名属性,例如因为名称是一个保留标识符(例如 `class`)
* 你不知道事先预期哪些其他属性
* 您希望将每个属性转发到另一个组件
所有这些情况在 Svelte 4 中都需要特殊的语法:
* 重命名:`export { klass as class}`
* 其他属性: `$$restProps`
* 所有属性 `$$props`
在 Svelte 5 中,`$props` 符文使得这变得简单直接,无需任何额外的 Svelte 特定语法:
* 重命名:使用属性重命名 `let { class: klass } = $props();`
* 其他属性:使用扩散 `let { foo, bar, ...rest } = $props();`
* 所有属性:不要解构 `let props = $props();`
```svelte
```
> \[!详细信息\] 我们为什么要这样做 `export let` 是更具争议性的 API 决定之一,关于你是否应该考虑一个属性是 `export` 或 `import`,有很多争论。`$props` 没有这种特性。这也符合其他符文,一般的想法可以归结为“Svelte 中所有与响应性相关的东西都是一个符文”。
>
> 也存在许多关于`export let`的限制,这需要额外的 API,如上所示。`$props`将这一概念统一为一个语法概念,该概念在很大程度上依赖于常规 JavaScript 解构语法。
##
活动变更
事件处理器在 Svelte 5 中得到了改进。在 Svelte 4 中,我们使用`on:`指令将事件监听器附加到元素上,而在 Svelte 5 中,它们像任何其他属性一样(换句话说——去掉冒号):
```svelte
```
由于它们只是属性,您可以使用正常的缩写语法...
```svelte
```
...尽管在使用命名事件处理函数时,通常最好使用更具描述性的名称。
###
组件事件
在 Svelte 4 中,组件可以通过创建一个派发器来发射事件,使用 `createEventDispatcher`。
此函数在 Svelte 5 中已弃用。取而代之,组件应接受*回调属性* - 这意味着您将这些函数作为属性传递给这些组件:
```svelte
{
size += power---.detail---;
if (size > 75) burst = true;
}}
---on:---deflate={(power) => {
if (size > 0) size -= power---.detail---;
}}
/>
{#if burst}
💥
{:else}
🎈
{/if}
```
```svelte
Pump power: {power}
```
###
冒泡事件
而不是使用`