嫁吱裨 发表于 2025-6-1 21:03:55

Svelte 最新中文文档教程(22)—— Svelte 5 迁移指南

前言

Svelte,一个语法简洁、入门容易,面向未来的前端框架。
从 Svelte 诞生之初,就备受开发者的喜爱,根据统计,从 2019 年到 2024 年,连续 6 年一直是开发者最感兴趣的前端框架 No.1:

Svelte 以其独特的编译时优化机制著称,具有轻量级、高性能、易上手等特性,非常适合构建轻量级 Web 项目。
为了帮助大家学习 Svelte,我同时搭建了 Svelte 最新的中文文档站点。
如果需要进阶学习,也可以入手我的小册《Svelte 开发指南》,语法篇、实战篇、原理篇三大篇章带你系统掌握 Svelte!
欢迎围观我的“网页版朋友圈”、加入“冴羽·成长陪伴社群”,踏上“前端大佬成长之路”。
Svelte 5 迁移指南

Svelte 5 采用了全面改进的语法和响应性系统。虽然开始时可能看起来有所不同,但您很快会注意到许多相似之处。本指南详细介绍了这些变化,并向您展示如何升级。同时,我们还提供了关于我们为什么做出这些改变的信息。
您不必立即迁移至新语法 —— Svelte 5 仍然支持旧的 Svelte 4 语法,您可以将使用新语法的组件与使用旧语法的组件混合使用。我们预计很多人可以通过仅修改几行代码就完成升级。还有一个 迁移脚本 可以帮助您自动完成许多步骤。
响应性语法变化

Svelte 5 的核心是新的符文 API。符文基本上是编译器指令,告诉 Svelte 有关响应性的信息。在语法上,符文是以美元符号开头的函数。
let -> $state

在 Svelte 4 中,组件顶层的 let 声明是隐式响应式的。在 Svelte 5 中,事情变得更明确:当使用 $state 符文创建变量时,该变量是响应式的。让我们通过将计数器包装在 $state 中来迁移到符文模式:
其他方面没有变化。count 仍然是数字本身,您可以直接读写它,没有 .value 或 getCount() 这样的包装器。
[!DETAILS] 我们为什么这样做
let 在顶层隐式声明响应式工作良好,但这意味着响应性受到限制——在其他地方的 let 声明都不是响应式的。这迫使您在重构代码以便复用时不得不使用 store。这意味着您必须学习一个完全不同的响应模型,结果通常并不那么好用。由于 Svelte 5 中的响应性更明确,您可以在组件顶层之外继续使用相同的 API。请前往 教程 了解更多信息。
$: -> $derived/$effect

在 Svelte 4 中,组件顶层的 $: 语句可用于声明派生,即完全通过其他状态的计算来定义的状态。在 Svelte 5 中,可以使用 $derived 符文实现这一点:
与 $state 一样,其他方面没有变化。double 仍然是数字本身,您可以直接读取它,而不需要像 .value 或 getDouble() 这样的包装器。
$: 语句还可以用于创建副作用。在 Svelte 5 中,可以使用 $effect 符文实现这一点:
[!DETAILS] 我们为什么这样做
$: 是一个很好的简写,容易上手:您可以在大多数代码前加上 $: 它就能以某种方式工作。这种直观性也是它的缺点,因为您的代码变得更复杂时,它并不那么好理解。代码的意图是创建一个派生,还是创建一个副作用?使用 $derived 和 $effect,您需要进行更多的前期决策(剧透:90% 的时候您想要 $derived),但将来您和团队中的其他开发人员会更容易理解。
还有一些难以发现的陷阱:

[*]$: 仅在渲染之前直接更新,这意味着在重新渲染之间你可能会读取到过时的值
[*]$: 仅在每个 tick 中运行一次,这意味着语句的运行频率可能低于你的预期
[*]$: 依赖关系是通过对依赖项的静态分析确定的。这在大多数情况下有效,但在重构过程中可能会以微妙的方式出错,例如依赖项被移动到一个函数中,从而不再可见
[*]$: 语句的顺序也是通过对依赖项的静态分析来确定的。在某些情况下可能会出现平局,导致排序错误,需要手动干预。在重构代码时,顺序也可能会出错,某些依赖项因此不再可见。
最后,它对 TypeScript 不友好(我们的编辑器工具需要跳过一些环节才能使其对 TypeScript 有效),这是使 Svelte 的响应模型真正通用的障碍。
$derived 和 $effect 解决了所有这些问题:

[*]始终返回最新值
[*]根据需要运行以保持稳定
[*]在运行时确定依赖关系,因此对重构免疫
[*]根据需要执行依赖关系,因此免受排序问题的影响
[*]对于 TypeScript 友好
export let -> $props

在 Svelte 4 中,组件的属性是通过 export let 声明的。每个属性都是一个声明。在 Svelte 5 中,所有属性都是通过 $props 符文声明的,通过解构:
在某些情况下,声明属性变得不如有几个 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();
<button class={klass} {...---$$restProps---+++rest+++}>点击我</button>[!DETAILS] 我们为什么这样做
export let 是一个颇具争议的 API 决策,围绕您是否应该考虑属性被 export 或 import 存在了很多争论。$props 没有这种特性。这也与其他符文保持一致,总体思路简化为“在 Svelte 中,所有与响应性有关的都是符文”。
export let 还存在许多局限性,需要额外的 API 去解决,如上所示。$props 将这些统一为一个语法概念,严重依赖常规的 JavaScript 解构语法
事件变化

在 Svelte 5 中,事件处理程序进行了改头换面。在 Svelte 4 中,我们使用 on: 指令将事件监听器附加到元素上,而在 Svelte 5 中,它们像其他属性一样(换句话说 —— 去掉冒号):
<button on---:---click={() => count++}>
<button {onclick}>
        点击次数:{count}
</button>
</button>由于它们只是属性,您可以使用正常的简写语法...
<button {onclick}>
        点击次数:{count}
</button>...尽管在使用命名事件处理函数时,通常最好使用更具描述性的名称。
组件事件

在 Svelte 4 中,组件可以使用 createEventDispatcher 创建一个调度器来发出事件。
该函数在 Svelte 5 中已弃用。相反,组件应接受 回调属性 —— 这意味着您可以将函数作为属性传递给这些组件:
{                size += power---.detail---;                if (size > 75) burst = true;        }}        ---on:---deflate={(power) => {                if (size > 0) size -= power---.detail---;        }}/>{#if burst}        新气球        <span >
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: Svelte 最新中文文档教程(22)—— Svelte 5 迁移指南