You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
+++import{ mount }from'svelte';+++importAppfrom'./App.svelte'---constapp=newApp({target: document.getElementById("app")});---+++constapp=mount(App,{target: document.getElementById("app")});+++exportdefaultapp;
mount 和 hydrate 具有完全相同的 API。不同之处在于 hydrate 会在其目标内提取 Svelte 的服务端渲染 HTML 并进行水合。两者都返回一个包含组件导出的对象以及可能的属性访问器(如果编译时使用 accessors: true)。它们不包含您可能熟悉的类组件 API 中的 $on、$set 和 $destroy 方法。这些是它的替代品:
对于 $on,不要监听事件,而是通过 events 属性在选项参数中传递它们。
+++import{ mount }from'svelte';+++importAppfrom'./App.svelte'---constapp=newApp({target: document.getElementById("app")});app.$on('event',callback);---+++constapp=mount(App,{target: document.getElementById("app"),events: {event: callback}});+++
前言
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()
这样的包装器。$: -> $derived/$effect
在 Svelte 4 中,组件顶层的
$:
语句可用于声明派生,即完全通过其他状态的计算来定义的状态。在 Svelte 5 中,可以使用$derived
符文实现这一点:与
$state
一样,其他方面没有变化。double
仍然是数字本身,您可以直接读取它,而不需要像.value
或getDouble()
这样的包装器。$:
语句还可以用于创建副作用。在 Svelte 5 中,可以使用$effect
符文实现这一点: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();
事件变化
在 Svelte 5 中,事件处理程序进行了改头换面。在 Svelte 4 中,我们使用
on:
指令将事件监听器附加到元素上,而在 Svelte 5 中,它们像其他属性一样(换句话说 —— 去掉冒号):由于它们只是属性,您可以使用正常的简写语法...
...尽管在使用命名事件处理函数时,通常最好使用更具描述性的名称。
组件事件
在 Svelte 4 中,组件可以使用
createEventDispatcher
创建一个调度器来发出事件。该函数在 Svelte 5 中已弃用。相反,组件应接受 回调属性 —— 这意味着您可以将函数作为属性传递给这些组件:
事件冒泡
组件应该接受一个
onclick
回调属性,而不是通过<button on:click>
将事件从元素“转发”到组件:请注意,这也意味着您可以将事件处理程序与其他属性一起“展开”到元素上,而不必繁琐地单独转发每个事件:
事件修饰符
在 Svelte 4 中,您可以向事件处理程序添加事件修饰符:
修饰符特定于
on:
,因此不适用于现代事件处理程序。在处理程序内部添加event.preventDefault()
等内容是更可取的,因为所有逻辑都集中在一个地方,而不是拆分在处理程序和修饰符之间。由于事件处理程序只是函数,您可以根据需要创建自己的封装:
有三个修饰符——
capture
、passive
和nonpassive
—— 不能被表示为包装函数,因为它们需要在事件处理程序绑定时应用,而不是在运行时应用。对于
capture
,我们将修饰符添加到事件名称中:更改事件处理程序的
passive
选项并不是轻而易举的事情。如果您有此用例——您可能没有!——那么您需要使用一个 action 来自己应用事件处理程序。多个事件处理程序
在 Svelte 4 中,这样做是可以的:
元素上的重复特性/属性 —— 现在包括事件处理程序 —— 是不允许的。相反,请改为这样做:
在展开属性时,本地事件处理程序必须在展开之后,否则可能会被覆盖:
代码片段而非插槽
在 Svelte 4 中,可以使用插槽将内容传递给组件。Svelte 5 用更强大和灵活的代码片段替换了它们,因此插槽在 Svelte 5 中被弃用。
不过,它们仍然可以继续使用,您可以在组件中混合使用代码片段和插槽。
在使用自定义元素时,您仍然应该像以前一样使用
<slot />
。在未来的版本中,当 Svelte 移除其内部版本的插槽时,它将保持这些插槽不变,即输出一个常规的 DOM 标签,而不是进行转换。默认内容
在 Svelte 4 中,传递 UI 给子组件的最简单方法是使用一个
<slot />
。在 Svelte 5 中,改为使用children
属性,然后通过{@render children()}
显示:多个内容占位符
如果您想要多个 UI 占位符,您必须使用命名插槽。在 Svelte 5 中,改为使用 props,随意命名它们,并
{@render ...}
它们:向上传递数据
在 Svelte 4 中,您将数据传递给
<slot />
,然后在父组件中使用let:
检索它。在 Svelte 5 中,代码片段承担了这一责任:迁移脚本
到目前为止,您应该对之前/之后的情况以及旧语法与新语法的关系有了相当不错的理解。您可能也意识到了,很多迁移都是相当技术性和重复的,您并不想手动完成这些事情。
我们也是这样认为的,这就是为什么我们提供了迁移脚本,用于自动完成大部分迁移。您可以使用
npx sv migrate svelte-5
升级您的项目。这将执行以下操作:package.json
中的核心依赖项let
->$state
等)on:click
->onclick
)<slot />
->{@render children()}
)<div slot="x">...</div>
->{#snippet x()}<div>...</div>{/snippet}
)new Component(...)
->mount(Component, ...)
)您还可以通过 VS Code 中的
Migrate Component to Svelte 5 Syntax
命令迁移单个组件,或在我们的 Playground 中通过Migrate
按钮完成。并非所有内容都可以自动迁移,一些迁移在之后需要手动清理。以下部分将更详细地描述这些内容。
run
您可能会看到迁移脚本将一些
$:
语句转换为从svelte/legacy
导入的run
函数。如果迁移脚本无法可靠地将语句迁移到$derived
并得出结论这是一个副作用,就会发生这种情况。在某些情况下,这可能是错误的,最好将其改为使用
$derived
。在其他情况下,这可能是正确的,但由于$:
语句在服务端也会运行,而$effect
不会,因此不能安全地转换它。于是,run
被用作权宜之计。run
模拟了$:
的大多数特征,因为它在服务端上运行一次,并在客户端作为$effect.pre
运行($effect.pre
在更改应用于 DOM 之前运行;您最有可能想要使用$effect
代替)。事件修饰符
事件修饰符不适用于事件属性(例如,您不能做
onclick|preventDefault={...}
)。因此,当将事件指令迁移到事件属性时,我们需要一个函数替代这些修饰符。这些从svelte/legacy
中导入,帮助支持迁移,例如仅使用event.preventDefault()
。不会自动迁移的内容
迁移脚本不会转换
createEventDispatcher
。您需要手动调整这些部分。之所以这样做,是因为风险太大,可能会导致组件出现故障,而迁移脚本无法发现这一点。迁移脚本不会转换
beforeUpdate/afterUpdate
。之所以这样做,是因为无法确定代码的实际意图。作为经验法则,您通常可以结合使用$effect.pre
(在与beforeUpdate
同时运行)和tick
(从svelte
导入,让您等到更改应用于 DOM,然后再进行一些工作)。组件不再是类
在 Svelte 3 和 4 中,组件是类。在 Svelte 5 中,它们是函数,应该以不同方式实例化。如果您需要手动实例化组件,您应该使用
mount
或hydrate
(从svelte
导入)。如果您在使用 SvelteKit 时看到此错误,请先尝试更新到最新版本的 SvelteKit,该版本添加了对 Svelte 5 的支持。如果您在没有 SvelteKit 的情况下使用 Svelte,您可能会有一个main.js
文件(或类似的文件),您需要进行调整:mount
和hydrate
具有完全相同的 API。不同之处在于hydrate
会在其目标内提取 Svelte 的服务端渲染 HTML 并进行水合。两者都返回一个包含组件导出的对象以及可能的属性访问器(如果编译时使用accessors: true
)。它们不包含您可能熟悉的类组件 API 中的$on
、$set
和$destroy
方法。这些是它的替代品:对于
$on
,不要监听事件,而是通过events
属性在选项参数中传递它们。对于
$set
,请使用$state
来创建一个响应式属性对象并进行操作。如果您在.js
或.ts
文件中执行此操作,请调整文件结尾包含.svelte
,即.svelte.js
或.svelte.ts
。对于
$destroy
,请使用unmount
代替。作为权宜之计,您还可以使用
createClassComponent
或asClassComponent
(从svelte/legacy
导入)来保持 保持在实例化后与 Svelte 4 相同的 API。如果这个组件不在您的控制之下,您可以使用
compatibility.componentApi
编译器选项来实现向后兼容性,这意味着使用new Component(...)
的代码可以在不做调整的情况下继续工作(请注意,这会给每个组件增加一些开销)。这还将为您通过bind:this
获取的所有组件实例添加$set
和$on
方法。注意
mount
和hydrate
不是同步的,因此类似onMount
这样的内容在函数返回时不会被调用,待处理的 Promise 块尚未呈现(因为#await
等待一个微任务以等待一个可能立即 resolve 的 Promise)。如果您需要这个保证,在调用mount/hydrate
之后调用flushSync
(从'svelte'
导入)。服务端 API 变化
同样,组件在服务端渲染编译时,不再具有
render
方法。相反,将函数传递给svelte/server
的render
:在 Svelte 4 中,将组件渲染为字符串也会返回所有组件的 CSS。在 Svelte 5 中,默认情况下不再这样,因为大多数情况下您使用工具链以其他方式处理它(例如 SvelteKit)。如果您需要从
render
返回 CSS,您可以将css
编译器选项设置为'injected'
,它将在head
中添加<style>
元素。组件类型变化
从类到函数的变化也反映在类型定义中:
SvelteComponent
,Svelte 4 的基类已被弃用,取而代之的是新的Component
类型,它定义了 Svelte 组件的函数形状。要在d.ts
文件中手动定义组件形状:声明某种类型的组件是必需的:
两个工具类型
ComponentEvents
和ComponentType
已被弃用。因为事件现在被定义为回调属性,而ComponentEvents
已过时,因为新的Component
类型已经是组件类型(例如ComponentType<SvelteComponent<{ prop: string }>>
==Component<{ prop: string }>
)。bind:this 变化
由于组件不再是类,使用
bind:this
不再返回带有$set
、$on
和$destroy
方法的类实例。它仅返回实例导出(export function/const
),并且如果您使用accessors
选项,则返回每个属性的 getter/setter 对。空格处理变化
此前,Svelte 使用了一个非常复杂的算法来确定是否应该保留空格。Svelte 5 简化了这一点,使开发人员更容易理解。规则如下:
pre
标签内保留空格和之前一样,您可以通过在编译器设置中将
preserveWhitespace
选项设置为true
或在<svelte:options>
中针对每个组件设置来禁用空格修剪。需要现代浏览器
Svelte 5 需要现代浏览器(换句话说,不支持 Internet Explorer),原因如下:
Proxies
clientWidth
/clientHeight
/offsetWidth
/offsetHeight
绑定的元素使用ResizeObserver
,而不是复杂的<iframe>
技巧<input type="range" bind:value={...} />
仅使用input
事件监听器,而不是同时监听change
事件作为后备方案legacy 编译器选项(该选项生成体积较大但兼容 IE 的代码)不再存在。
编译器选项的变化
false
/true
(之前已经弃用)和"none"
这些有效值legacy
选项被重新调整用途hydratable
选项已被移除。Svelte 组件现在始终是可水合的enableSourcemap
选项已被移除。现在始终生成 source map,工具可以选择忽略它tag
选项已被移除。请改用组件内的<svelte:options customElement="tag-name" />
loopGuardTimeout
、format
、sveltePath
、errorMode
和varsReport
选项已被移除children
属性被保留组件标签里的内容变为名为
children
的代码片段属性。你不能使用相同的名称定义其他属性。点符号表示组件
在 Svelte 4 中,
<foo.bar>
将创建一个标签名为"foo.bar"
的元素。在 Svelte 5 中,foo.bar
被视为组件。这在each
块中特别有用:符文模式中的重大变化
某些重大变更仅在组件处于符文模式时才适用。
不允许绑定到组件导出
符文模式下,组件的导出不能直接绑定。例如,组件
A
中有export const foo = ...
,然后执行<A bind:foo />
,将导致错误。使用bind:this
代替——<A bind:this={a} />
——并通过a.foo
访问导出。此更改使事情更容易理解,因为它强制了属性和导出之间的清晰分离。绑定需要使用
$bindable()
显式定义在 Svelte 4 语法中,每个属性(通过
export let
声明)都是可绑定的,这意味着您可以对其使用bind:
。在符文模式中,属性默认不具有可绑定性:您需要使用$bindable
符文来标记可绑定的 props。如果一个可绑定属性有默认值(例如
let { foo = $bindable('bar') } = $props();
),当你要绑定该属性时,需要传递一个非undefined
的值。这可以防止出现模棱两可的行为 —— 父组件和子组件必须具有相同的值,并能获得更好的性能(在 Svelte 4 中,默认值被反映回父组件,导致额外的无用渲染周期)。accessors
选项被忽略将
accessors
选项设置为true
可使组件的属性在组件实例上直接访问。在符文模式下,属性永远不会在组件实例上访问。如果您需要暴露它们,可以使用组件导出。immutable
选项被忽略在符文模式下,设置
immutable
选项没有效果。这个概念被$state
及其变体的工作方式所替代。类不再是“自动响应式”
在 Svelte 4 中,执行以下操作会触发响应性:
这是因为 Svelte 编译器将对
foo.value
的赋值视为更新所有引用foo
的内容的指令。在 Svelte 5 中,响应性在运行时而不是编译时确定,因此您应该将value
定义为Foo
类上的响应式$state
字段。将new Foo()
包装在$state(...)
中将不会产生任何效果——只有简单的对象和数组会被深度响应式化。<svelte:component>
不再必要在 Svelte 4 中,组件是 静态的 —— 如果您渲染
<Thing>
,并且Thing
的值发生变化,不会发生任何事情。要使其动态,必须使用<svelte:component>
。在 Svelte 5 中,这不再成立:
触控和滚轮事件是 passive
当使用
onwheel
、onmousewheel
、ontouchstart
和ontouchmove
事件属性时,处理程序是 passive,以符合浏览器默认行为。这极大地提高了响应能力,因为浏览器可以立即滚动文档,而不是等待查看事件处理程序是否调用event.preventDefault()
。在极少数需要阻止这些事件默认行为的情况下,你应该使用
on
来代替(例如在 action 内部)。Attribute / prop 语法更严格
在 Svelte 4 中,复杂的属性值不需要加引号:
这是一个潜在问题。在符文模式下,如果您希望连接内容,必须将值放在引号中:
注意,如果你在 Svelte 5 中使用引号包裹单个表达式(例如
answer="{42}"
),也会收到警告 —— 在 Svelte 6 中,这将导致值被转换为字符串,而不是作为数字传递。HTML 结构更严格
在Svelte 4中,你可以编写一些在服务器端渲染时会被浏览器修复的HTML代码。例如你可以这样写...
...浏览器将自动插入
<tbody>
元素:Svelte 5 对 HTML 结构的要求更加严格,在浏览器会修复 DOM 的情况下会抛出编译错误。
其他重大变化
更严格的
@const
赋值验证不再允许对const声明的解构部分进行赋值。这种操作本就不应该被允许。
:is(...) 和 :where(...) 是作用域的
以前,Svelte 不分析
:is(...)
和:where(...)
内部的选择器,实际上会将它们视为全局选择器。Svelte 5 会在当前组件的上下文中分析它们。因此,如果某些选择器依赖于这种处理方式,现在可能会被视为未使用。要修复这个问题,请在:is(...)/:where(...)
选择器内使用:global(...)
。在使用 Tailwind 的
@apply
指令时,添加:global
选择器以保留使用 Tailwind 生成的:is(...)
选择器的规则:CSS 哈希位置不再具有确定性
以前,Svelte 总是会在最后插入 CSS 哈希值。在 Svelte 5 中这一点不再有保证。这只有在 有非常奇怪的 css 选择器 时才会导致问题。
作用域 CSS 使用 :where(...)
为了避免由不可预测的特异性变化引起的问题,作用域 CSS 选择器现在使用
.svelte-xyz123
(其中xyz123
如前所述,是<style>
内容的哈希)旁边使用:where(.svelte-xyz123)
选择器修饰符。您可以在 这里 阅读更多细节。如果您需要支持不实现
:where
的古老浏览器,您可以手动修改生成的 CSS,但代价是会产生不可预测的特异性变化:错误/警告代码已重命名
错误和警告代码已重命名。以前它们使用破折号分隔单词,现在使用下划线(例如,foo-bar 变为 foo_bar)。此外,一些代码的措辞也略有改动。
命名空间数量减少
您可以传递给编译器选项
namespace
的有效命名空间数量减少到html
(默认)、mathml
和svg
。foreign
命名空间仅对 Svelte Native 有用,我们计划在 5.x 次要版本中以不同方式支持它。beforeUpdate/afterUpdate 变更
如果
beforeUpdate
修改了模板中引用的变量,则在初始渲染时不再运行两次。父组件中的
afterUpdate
回调现在将在任何子组件的afterUpdate
回调之后运行。当组件包含
<slot>
且其内容更新时,beforeUpdate/afterUpdate
不再运行。这两个函数在符文模式下被禁止 —— 请改为使用
$effect.pre(...)
和$effect(...)
。contenteditable
行为变化如果您有一个
contenteditable
节点,并且有一个对应的绑定 和 一个响应式值(例如:<div contenteditable=true bind:textContent>count is {count}</div>
),那么contenteditable 内的值不会因count
的更新而更新,因为绑定会立即完全控制内容,且内容应该只通过绑定来更新。oneventname
属性不再接受字符串值在Svelte 4中,可以将 HTML 元素的事件属性指定为字符串:
不推荐这种做法,在 Svelte 5 中已不再可用,其中
onclick
属性替代on:click
成为添加事件处理程序的机制。null
和undefined
变为空字符串在 Svelte 4 中,
null
和undefined
会被打印为对应的字符串。在 100 个案例中,99 次您希望将其变为空字符串,而这也是其他大多数框架所做的。因此,在 Svelte 5 中,null
和undefined
变为空字符串。bind:files
值只能是null
、undefined
或FileList
bind:files
现在是一个双向绑定。因此,在设置值时,它需要是 假值(null
或undefined
)或FileList
类型。绑定现在会响应表单重置
之前,绑定不会考虑表单的
reset
事件,因此值可能会与 DOM 不同步。Svelte 5 通过在文档上放置reset
监听器并在必要时调用绑定来修复这个问题。walk 不再导出
svelte/compiler
为了方便从estree-walker
重新导出了walk
。在 Svelte 5 中,这种情况不再存在,如果需要请直接从该包中导入。在
svelte:options
里的内容被禁止在 Svelte 4 中,您可以在
<svelte:options />
标签内写入内容。它会被忽略,但您可以在里面写一些东西。在 Svelte 5 中,该标签里的内容会导致编译错误。声明式 shadow roots 中的
<slot>
元素会被保留Svelte 4 在所有地方都用自己版本的插槽替换了
<slot />
标签。Svelte 5 在这些标签作为<template shadowrootmode="...">
元素的子元素时会保留它们。<svelte:element>
标签必须是表达式在 Svelte 4 中,
<svelte:element this="div">
是有效的代码。这没有什么意义——您应该直接使用<div>
。在极少数确实需要使用字面值的情况下,你可以这样做:请注意,虽然 Svelte 4 会将
<svelte:element this="input">
(举例)与<input>
视为相同,以确定可以应用哪些bind:
指令。但 Svelte 5 不会这样做。mount
默认播放过渡效果用于渲染组件树的
mount
函数默认播放过渡效果,除非将intro
选项设置为false
。这与传统的类组件不同,后者在手动实例化时默认不播放过渡效果。<img src={...}>
和{@html ...}
水合不匹配不会被修复在 Svelte 4 中,如果
src
属性或{@html ...}
标签的值在服务端和客户端不同(即水合不匹配),这种不匹配会被修复。这个过程代价很高:设置src
属性(即使它计算出相同的结果)会导致图像和 iframe 被重新加载,并且重新插入大量 HTML 是缓慢的。由于这些不匹配极为罕见,Svelte 5 假定这些值保持不变,但在开发环境中如果它们不匹配会向你发出警告。要强制更新,你可以这样做:
水合行为不同
Svelte 5 在服务端渲染期间使用注释,这些注释用于在客户端进行更健壮和高效的水合。因此,如果您打算对其进行水合,您不应该删除HTML输出中的注释,如果您手动编写了要由 Svelte 组件水合的 HTML,则需要在正确的位置添加这些注释。
onevent
属性被委托事件属性替代事件指令:使用
onclick={handler}
而不是on:click={handler}
。为了向后兼容,on:event
语法仍然受到支持,并且行为与 Svelte 4 中相同。然而,某些onevent
属性是被委托的,这意味着您需要注意不要手动停止这些事件的传播,因为它们可能永远不会传递到根节点的该事件类型的监听器。--style-props
使用了不同的元素在使用 CSS 自定义属性时,
Svelte 5
使用额外的<svelte-css-wrapper>
元素而不是<div>
来包装组件。Svelte 中文文档
点击查看中文文档 - Svelte 5迁移指南
系统学习 Svelte,欢迎入手小册《Svelte 开发指南》。语法篇、实战篇、原理篇三大篇章带你系统掌握 Svelte!
此外我还写过 JavaScript 系列、TypeScript 系列、React 系列、Next.js 系列、冴羽答读者问等 14 个系列文章, 全系列文章目录:https://github.com/mqyqingfeng/Blog
欢迎围观我的“网页版朋友圈”、加入“冴羽·成长陪伴社群”,踏上“前端大佬成长之路”。
The text was updated successfully, but these errors were encountered: