Svelte 5 迁移指南
版本 5 带有彻底改进的语法和反应系统。虽然乍一看可能有所不同,但你很快就会注意到许多相似之处。本指南详细介绍了这些变化并向你展示了如何升级。除此之外,我们还提供有关为什么进行这些更改的信息。
¥Version 5 comes with an overhauled syntax and reactivity system. While it may look different at first, you’ll soon notice many similarities. This guide goes over the changes in detail and shows you how to upgrade. Along with it, we also provide information on why we did these changes.
你不必立即迁移到新语法 - Svelte 5 仍然支持旧的 Svelte 4 语法,你可以将使用新语法的组件与使用旧语法的组件混合搭配,反之亦然。我们预计许多人能够仅通过最初更改几行代码即可升级。还有一个 迁移脚本 可以自动帮助你完成许多这些步骤。
¥You don’t have to migrate to the new syntax right away - Svelte 5 still supports the old Svelte 4 syntax, and you can mix and match components using the new syntax with components using the old and vice versa. We expect many people to be able to upgrade with only a few lines of code changed initially. There’s also a migration script that helps you with many of these steps automatically.
反应式语法更改(Reactivity syntax changes)
¥Reactivity syntax changes
Svelte 5 的核心是新的符文 API。符文基本上是编译器指令,用于通知 Svelte 有关反应性的信息。从语法上讲,符文是以美元符号开头的函数。
¥At the heart of Svelte 5 is the new runes API. Runes are basically compiler instructions that inform Svelte about reactivity. Syntactically, runes are functions starting with a dollar-sign.
let -> $state
在 Svelte 4 中,组件顶层的 let
声明是隐式响应的。在 Svelte 5 中,情况更加明确:使用 $state
符文创建时,变量是反应性的。让我们通过将计数器封装在 $state
中将计数器迁移到符文模式:
¥In Svelte 4, a let
declaration at the top level of a component was implicitly reactive. In Svelte 5, things are more explicit: a variable is reactive when created using the $state
rune. Let’s migrate the counter to runes mode by wrapping the counter in $state
:
<script>
let count = $state(0);
</script>
其他内容均无变化。count
仍然是数字本身,你可以直接读取和写入它,而不需要像 .value
或 getCount()
这样的封装器。
¥Nothing else changes. count
is still the number itself, and you read and write directly to it, without a wrapper like .value
or getCount()
.
我们为什么这样做
let
在顶层隐式响应效果很好,但这意味着响应性受到限制 - 其他任何地方的let
声明都不是反应性的。这迫使你在重构顶层组件中的代码以供重用时不得不使用存储。这意味着你必须学习一个完全独立的反应模型,而且结果通常不太好用。由于 Svelte 5 中的反应性更加明确,因此你可以在顶层组件之外继续使用相同的 API。前往 教程 了解更多信息。
¥[!DETAILS] Why we did this
let
being implicitly reactive at the top level worked great, but it meant that reactivity was constrained - alet
declaration anywhere else was not reactive. This forced you to resort to using stores when refactoring code out of the top level of components for reuse. This meant you had to learn an entirely separate reactivity model, and the result often wasn’t as nice to work with. Because reactivity is more explicit in Svelte 5, you can keep using the same API outside the top level of components. Head to the tutorial to learn more.
$: -> $derived/$effect
在 Svelte 4 中,组件顶层的 $:
语句可用于声明派生,即完全通过计算其他状态来定义的状态。在 Svelte 5 中,这是使用 $derived
符文实现的:
¥In Svelte 4, a $:
statement at the top level of a component could be used to declare a derivation, i.e. state that is entirely defined through a computation of other state. In Svelte 5, this is achieved using the $derived
rune:
<script>
let count = $state(0);
$: const double = $derived(count * 2);
</script>
与 $state
一样,其他一切都没有变化。double
仍然是数字本身,你可以直接读取它,而不需要像 .value
或 getDouble()
这样的封装器。
¥As with $state
, nothing else changes. double
is still the number itself, and you read it directly, without a wrapper like .value
or getDouble()
.
$:
语句也可用于创建副作用。在 Svelte 5 中,这是使用 $effect
符文实现的:
¥A $:
statement could also be used to create side effects. In Svelte 5, this is achieved using the $effect
rune:
<script>
let count = $state(0);
$:$effect(() => {
if (count > 5) {
alert('Count is too high!');
}
});
</script>
我们为什么这样做
$:
是一种很好的简写,很容易上手:你可以在大多数代码前面加上$:
,它会以某种方式工作。这种直观性也是它的缺点,因为你的代码变得越复杂,它就越不容易推断。代码的意图是创建派生还是副作用?使用$derived
和$effect
,你需要进行更多的前期决策(剧透警告:90% 的时间你想要$derived
),但未来的你和你团队中的其他开发者将更轻松。
¥[!DETAILS] Why we did this
$:
was a great shorthand and easy to get started with: you could slap a$:
in front of most code and it would somehow work. This intuitiveness was also its drawback the more complicated your code became, because it wasn’t as easy to reason about. Was the intent of the code to create a derivation, or a side effect? With$derived
and$effect
, you have a bit more up-front decision making to do (spoiler alert: 90% of the time you want$derived
), but future-you and other developers on your team will have an easier time.还有一些难以发现的陷阱:
¥There were also gotchas that were hard to spot:
$:
仅在渲染之前直接更新,这意味着你可以在重新渲染之间读取陈旧的值¥
$:
only updated directly before rendering, which meant you could read stale values in-between rerenders
$:
每次运行一次,这意味着语句运行的频率可能比你想象的要低¥
$:
only ran once per tick, which meant that statements may run less often than you think
$:
依赖是通过对依赖的静态分析确定的。这在大多数情况下都有效,但在重构期间可能会以微妙的方式中断,例如,依赖将被移入函数中,因此不再可见¥
$:
dependencies were determined through static analysis of the dependencies. This worked in most cases, but could break in subtle ways during a refactoring where dependencies would be for example moved into a function and no longer be visible as a result
$:
语句也通过使用依赖的静态分析进行排序。在某些情况下,可能会出现平局,因此排序会出错,需要手动干预。重构代码时排序也可能会中断,因此某些依赖不再可见。¥
$:
statements were also ordered by using static analysis of the dependencies. In some cases there could be ties and the ordering would be wrong as a result, needing manual interventions. Ordering could also break while refactoring code and some dependencies no longer being visible as a result.最后,它不是 TypeScript 友好的(我们的编辑器工具必须经过一些考验才能使其对 TypeScript 有效),这阻碍了 Svelte 的反应模型真正通用。
¥Lastly, it wasn’t TypeScript-friendly (our editor tooling had to jump through some hoops to make it valid for TypeScript), which was a blocker for making Svelte’s reactivity model truly universal.
$derived
和$effect
通过以下方式修复所有这些问题¥
$derived
and$effect
fix all of these by
始终返回最新值
¥always returning the latest value
按需要频繁运行以保持稳定
¥running as often as needed to be stable
在运行时确定依赖,因此不受重构的影响
¥determining the dependencies at runtime, and therefore being immune to refactorings
根据需要执行依赖,因此不受排序问题的影响
¥executing dependencies as needed and therefore being immune to ordering problems
TypeScript 友好
¥being TypeScript-friendly
export let -> $props
在 Svelte 4 中,组件的属性使用 export let
声明。每个属性都是一个声明。在 Svelte 5 中,所有属性都通过 $props
符文通过解构声明:
¥In Svelte 4, properties of a component were declared using export let
. Each property was one declaration. In Svelte 5, all properties are declared through the $props
rune, through destructuring:
<script>
export let optional = 'unset';
export let required;
let { optional = 'unset', required } = $props();
</script>
在多种情况下,声明属性变得不如拥有几个 export let
声明那么简单:
¥There are multiple cases where declaring properties becomes less straightforward than having a few export let
declarations:
你想重命名该属性,例如因为名称是保留标识符(例如
class
)¥you want to rename the property, for example because the name is a reserved identifier (e.g.
class
)你不知道提前会期待哪些其他属性
¥you don’t know which other properties to expect in advance
你想将每个属性转发到另一个组件
¥you want to forward every property to another component
所有这些情况都需要 Svelte 4 中的特殊语法:
¥All these cases need special syntax in Svelte 4:
重置 元素
export { klass as class}
¥renaming:
export { klass as class}
paraglide (i18n)
$$restProps
所有属性
$$props
¥all properties
$$props
在 Svelte 5 中,$props
符文使这变得简单,无需任何额外的 Svelte 特定语法:
¥In Svelte 5, the $props
rune makes this straightforward without any additional Svelte-specific syntax:
重置 元素使用属性重命名
let { class: klass } = $props();
¥renaming: use property renaming
let { class: klass } = $props();
paraglide (i18n)使用扩展
let { foo, bar, ...rest } = $props();
¥other properties: use spreading
let { foo, bar, ...rest } = $props();
所有属性:不要改变 props。
¥all properties: don’t destructure
let props = $props();
<script>
let klass = '';
export { klass as class};
let { class: klass, ...rest } = $props();
</script>
<button class={klass} {...$$restPropsrest}>click me</button>
我们为什么这样做
export let
是比较有争议的 API 决定之一,关于是否应该考虑属性是export
还是import
存在很多争论。$props
没有这个特性。它也与其他符文一致,一般思路归结为 “进一步阅读”。
¥[!DETAILS] Why we did this
export let
was one of the more controversial API decisions, and there was a lot of debate about whether you should think about a property beingexport
ed orimport
ed.$props
doesn’t have this trait. It’s also in line with the other runes, and the general thinking reduces to “everything special to reactivity in Svelte is a rune”.
export let
也有很多限制,需要额外的 API,如上所示。$props
尝试克隆给定的值以返回不再改变的引用。¥There were also a lot of limitations around
export let
, which required additional API, as shown above.$props
unite this in one syntactical concept that leans heavily on regular JavaScript destructuring syntax.
事件变化(Event changes)
¥Event changes
事件处理程序在 Svelte 5 中得到了改头换面。而在 Svelte 4 中,我们使用 on:
指令将事件监听器附加到元素,而在 Svelte 5 中,它们与其他任何属性一样(换句话说 - 删除冒号):
¥Event handlers have been given a facelift in Svelte 5. Whereas in Svelte 4 we use the on:
directive to attach an event listener to an element, in Svelte 5 they are properties like any other (in other words - remove the colon):
<script>
let count = $state(0);
</script>
<button on:click={() => count++}>
clicks: {count}
</button>
由于它们只是属性,因此你可以使用正常的简写语法……
¥Since they’re just properties, you can use the normal shorthand syntax...
<script>
let count = $state(0);
function onclick() {
count++;
}
</script>
<button {onclick}>
clicks: {count}
</button>
...但是当使用命名事件处理函数时,通常最好使用更具描述性的名称。
¥...though when using a named event handler function it’s usually better to use a more descriptive name.
组件事件(Component events)
¥Component events
在 Svelte 4 中,组件可以通过使用 createEventDispatcher
创建调度程序来触发事件。
¥In Svelte 4, components could emit events by creating a dispatcher with createEventDispatcher
.
此函数在 Svelte 5 中已弃用。相反,组件应该接受回调 props - 这意味着你将函数作为属性传递给这些组件:
¥This function is deprecated in Svelte 5. Instead, components should accept callback props - which means you then pass functions as properties to these components:
<script>
import Pump from './Pump.svelte';
let size = $state(15);
let burst = $state(false);
function reset() {
size = 15;
burst = false;
}
</script>
<Pump
on:inflate={(power) => {
size += power.detail;
if (size > 75) burst = true;
}}
on:deflate={(power) => {
if (size > 0) size -= power.detail;
}}
/>
{#if burst}
<button onclick={reset}>new balloon</button>
<span class="boom">💥</span>
{:else}
<span class="balloon" style="scale: {0.01 * size}">
🎈
</span>
{/if}
<script lang="ts">
import Pump from './Pump.svelte';
let size = $state(15);
let burst = $state(false);
function reset() {
size = 15;
burst = false;
}
</script>
<Pump
on:inflate={(power) => {
size += power.detail;
if (size > 75) burst = true;
}}
on:deflate={(power) => {
if (size > 0) size -= power.detail;
}}
/>
{#if burst}
<button onclick={reset}>new balloon</button>
<span class="boom">💥</span>
{:else}
<span class="balloon" style="scale: {0.01 * size}">
🎈
</span>
{/if}
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
let { inflate, deflate } = $props();
let power = $state(5);
</script>
<button onclick={() => dispatch('inflate', power)inflate(power)}>
inflate
</button>
<button onclick={() => dispatch('deflate', power)deflate(power)}>
deflate
</button>
<button onclick={() => power--}>-</button>
Pump power: {power}
<button onclick={() => power++}>+</button>
<script lang="ts">
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
let { inflate, deflate } = $props();
let power = $state(5);
</script>
<button onclick={() => dispatch('inflate', power)inflate(power)}>
inflate
</button>
<button onclick={() => dispatch('deflate', power)deflate(power)}>
deflate
</button>
<button onclick={() => power--}>-</button>
Pump power: {power}
<button onclick={() => power++}>+</button>
冒泡事件(Bubbling events)
¥Bubbling events
组件不应将事件从元素传递到组件,而应接受 onclick
回调 prop:
¥Instead of doing <button on:click>
to ‘forward’ the event from the element to the component, the component should accept an onclick
callback prop:
<script>
let { onclick } = $props();
</script>
<button on:click {onclick}>
click me
</button>
请注意,这也意味着你可以将 ‘spread’ 事件处理程序与其他属性一起放到元素上,而不是繁琐地单独转发每个事件:
¥Note that this also means you can ‘spread’ event handlers onto the element along with other props instead of tediously forwarding each event separately:
<script>
let props = $props();
</script>
<button {...$$props} on:click on:keydown on:all_the_other_stuff {...props}>
click me
</button>
事件修饰符(Event modifiers)
¥Event modifiers
在 Svelte 4 中,你可以向处理程序添加事件修饰符:
¥In Svelte 4, you can add event modifiers to handlers:
<button on:click|once|preventDefault={handler}>...</button>
修饰符特定于 on:
,因此不适用于现代事件处理程序。在处理程序本身内部添加诸如 event.preventDefault()
之类的内容是可取的,因为所有逻辑都集中在一个地方,而不是分散在处理程序和修饰符之间。
¥Modifiers are specific to on:
and as such do not work with modern event handlers. Adding things like event.preventDefault()
inside the handler itself is preferable, since all the logic lives in one place rather than being split between handler and modifiers.
由于事件处理程序只是函数,你可以根据需要创建自己的封装器:
¥Since event handlers are just functions, you can create your own wrappers as necessary:
<script>
function once(fn) {
return function (event) {
if (fn) fn.call(this, event);
fn = null;
};
}
function preventDefault(fn) {
return function (event) {
event.preventDefault();
fn.call(this, event);
};
}
</script>
<button onclick={once(preventDefault(handler))}>...</button>
有三个修饰符 — capture
、passive
和 nonpassive
— 不能表示为封装函数,因为它们需要在绑定事件处理程序时应用,而不是在运行时应用。
¥There are three modifiers — capture
, passive
and nonpassive
— that can’t be expressed as wrapper functions, since they need to be applied when the event handler is bound rather than when it runs.
对于 capture
,我们将修饰符添加到事件名称:
¥For capture
, we add the modifier to the event name:
<button onclickcapture={...}>...</button>
同时,更改事件处理程序的 passive
选项不是一件可以轻易完成的事情。如果你有它的用例 — 而你可能没有!— 那么你将需要使用操作来自己应用事件处理程序。
¥Changing the passive
option of an event handler, meanwhile, is not something to be done lightly. If you have a use case for it — and you probably don’t! — then you will need to use an action to apply the event handler yourself.
多个事件处理程序(Multiple event handlers)
¥Multiple event handlers
在 Svelte 4 中,这是可能的:
¥In Svelte 4, this is possible:
<button on:click={one} on:click={two}>...</button>
不允许在元素上重复属性/特性 - 现在包括事件处理程序。而是这样做:
¥Duplicate attributes/properties on elements — which now includes event handlers — are not allowed. Instead, do this:
<button
onclick={(e) => {
one(e);
two(e);
}}
>
...
</button>
当传播 props 时,本地事件处理程序必须在传播之后,否则它们有被覆盖的风险:
¥When spreading props, local event handlers must go after the spread, or they risk being overwritten:
<button
{...props}
onclick={(e) => {
doStuff(e);
props.onclick?.(e);
}}
>
...
</button>
我们为什么这样做
createEventDispatcher
总是有点样板化:
¥[!DETAILS] Why we did this
createEventDispatcher
was always a bit boilerplate-y:
继承来自
¥import the function
调用该函数以获取调度函数
¥call the function to get a dispatch function
使用字符串和可能的负载调用所述调度函数
¥call said dispatch function with a string and possibly a payload
通过
.detail
属性在另一端检索所述有效负载,因为事件本身始终是CustomEvent
¥retrieve said payload on the other end through a
.detail
property, because the event itself was always aCustomEvent
始终可以使用组件回调 props,但由于你必须使用
on:
监听 DOM 事件,因此出于语法一致性的考虑,对组件事件使用createEventDispatcher
是有意义的。现在我们有了事件属性(onclick
),情况正好相反:回调 props 现在是更明智的做法。¥It was always possible to use component callback props, but because you had to listen to DOM events using
on:
, it made sense to usecreateEventDispatcher
for component events due to syntactical consistency. Now that we have event attributes (onclick
), it’s the other way around: Callback props are now the more sensible thing to do.删除事件修饰符可以说是那些喜欢事件修饰符简写语法的人似乎倒退的变化之一。鉴于它们不经常使用,我们用较小的表面积换取更明确性。修饰符也不一致,因为它们中的大多数只能在 DOM 元素上使用。
¥The removal of event modifiers is arguably one of the changes that seems like a step back for those who’ve liked the shorthand syntax of event modifiers. Given that they are not used that frequently, we traded a smaller surface area for more explicitness. Modifiers also were inconsistent, because most of them were only useable on DOM elements.
同一事件的多个监听器也不再可能,但它无论如何都是一种反模式,因为它妨碍了可读性:如果有很多属性,除非它们彼此紧挨着,否则很难发现有两个处理程序。它还意味着两个处理程序是独立的,而实际上
one
内部的event.stopImmediatePropagation()
之类的东西会阻止调用two
。¥Multiple listeners for the same event are also no longer possible, but it was something of an anti-pattern anyway, since it impedes readability: if there are many attributes, it becomes harder to spot that there are two handlers unless they are right next to each other. It also implies that the two handlers are independent, when in fact something like
event.stopImmediatePropagation()
insideone
would preventtwo
from being called.通过弃用
createEventDispatcher
和on:
指令,转而使用回调 props 和普通元素属性,我们:¥By deprecating
createEventDispatcher
and theon:
directive in favour of callback props and normal element properties, we:
拒绝新请求()
¥reduce Svelte’s learning curve
删除样板,特别是围绕
createEventDispatcher
¥remove boilerplate, particularly around
createEventDispatcher
删除为可能甚至没有监听器的事件创建
CustomEvent
对象的开销¥remove the overhead of creating
CustomEvent
objects for events that may not even have listeners添加传播事件处理程序的能力
¥add the ability to spread event handlers
添加知道为组件提供了哪些事件处理程序的能力
¥add the ability to know which event handlers were provided to a component
添加表达给定事件处理程序是必需的还是可选的能力
¥add the ability to express whether a given event handler is required or optional
增加类型安全性(以前,Svelte 几乎不可能保证组件不会发出特定事件)
¥increase type safety (previously, it was effectively impossible for Svelte to guarantee that a component didn’t emit a particular event)
代码片段代替插槽(Snippets instead of slots)
¥Snippets instead of slots
在 Svelte 4 中,可以使用插槽将内容传递给组件。Svelte 5 用功能更强大、更灵活的代码片段取代了它们,因此 Svelte 5 中已弃用插槽。
¥In Svelte 4, content can be passed to components using slots. Svelte 5 replaces them with snippets which are more powerful and flexible, and as such slots are deprecated in Svelte 5.
但是,它们继续工作,你可以将代码片段传递给使用插槽的组件:
¥They continue to work, however, and you can pass snippets to a component that uses slots:
<slot />
<hr />
<slot name="foo" message="hello" />
<script>
import Child from './Child.svelte';
</script>
<Child>
default child content
{#snippet foo({ message })}
message from child: {message}
{/snippet}
</Child>
<script lang="ts">
import Child from './Child.svelte';
</script>
<Child>
default child content
{#snippet foo({ message })}
message from child: {message}
{/snippet}
</Child>
(反之则不然 - 你不能将插槽内容传递给使用 {@render ...}
标记的组件。)
¥(The reverse is not true — you cannot pass slotted content to a component that uses {@render ...}
tags.)
使用自定义元素时,你仍应像以前一样使用 <slot />
。在未来版本中,当 Svelte 删除其内部版本的插槽时,它将保留这些插槽原样,即输出常规 DOM 标签而不是对其进行转换。
¥When using custom elements, you should still use <slot />
like before. In a future version, when Svelte removes its internal version of slots, it will leave those slots as-is, i.e. output a regular DOM tag instead of transforming it.
默认内容(Default content)
¥Default content
在 Svelte 4 中,将 UI 传递给子组件的最简单方法是使用 <slot />
。在 Svelte 5 中,这是使用 children
prop 完成的,然后用 {@render children()}
显示:
¥In Svelte 4, the easiest way to pass a piece of UI to the child was using a <slot />
. In Svelte 5, this is done using the children
prop instead, which is then shown with {@render children()}
:
<script>
let { children } = $props();
</script>
<slot />
{@render children?.()}
多个内容占位符(Multiple content placeholders)
¥Multiple content placeholders
如果你想要多个 UI 占位符,则必须使用命名插槽。在 Svelte 5 中,改用 props,随意命名它们并 {@render ...}
它们:
¥If you wanted multiple UI placeholders, you had to use named slots. In Svelte 5, use props instead, name them however you like and {@render ...}
them:
<script>
let { header, main, footer } = $props();
</script>
<header>
<slot name="header" />
{@render header()}
</header>
<main>
<slot name="main" />
{@render main()}
</main>
<footer>
<slot name="footer" />
{@render footer()}
</footer>
传递数据备份(Passing data back up)
¥Passing data back up
在 Svelte 4 中,你会将数据传递给 <slot />
,然后使用父组件中的 let:
检索它。在 Svelte 5 中,代码片段承担了这一责任:
¥In Svelte 4, you would pass data to a <slot />
and then retrieve it with let:
in the parent component. In Svelte 5, snippets take on that responsibility:
<script>
import List from './List.svelte';
</script>
<List items={['one', 'two', 'three']} let:item>
{#snippet item(text)}
<span>{text}</span>
{/snippet}
<span slot="empty">No items yet</span>
{#snippet empty()}
<span>No items yet</span>
{/snippet}
</List>
<script lang="ts">
import List from './List.svelte';
</script>
<List items={['one', 'two', 'three']} let:item>
{#snippet item(text)}
<span>{text}</span>
{/snippet}
<span slot="empty">No items yet</span>
{#snippet empty()}
<span>No items yet</span>
{/snippet}
</List>
<script>
let { items, item, empty } = $props();
</script>
{#if items.length}
<ul>
{#each items as entry}
<li>
<slot item={entry} />
{@render item(entry)}
</li>
{/each}
</ul>
{:else}
<slot name="empty" />
{@render empty?.()}
{/if}
<script lang="ts">
let { items, item, empty } = $props();
</script>
{#if items.length}
<ul>
{#each items as entry}
<li>
<slot item={entry} />
{@render item(entry)}
</li>
{/each}
</ul>
{:else}
<slot name="empty" />
{@render empty?.()}
{/if}
我们为什么这样做 Slots 很容易上手,但用例越高级,语法就越复杂和混乱:
¥[!DETAILS] Why we did this Slots were easy to get started with, but the more advanced the use case became, the more involved and confusing the syntax became:
let:
语法让很多人感到困惑,因为它会创建一个变量,而所有其他:
指令都会接收一个变量¥the
let:
syntax was confusing to many people as it creates a variable whereas all other:
directives receive a variable用
let:
声明的变量的范围不明确。在上面的例子中,你可能看起来可以在empty
插槽中使用item
插槽 prop,但事实并非如此¥the scope of a variable declared with
let:
wasn’t clear. In the example above, it may look like you can use theitem
slot prop in theempty
slot, but that’s not true必须使用
slot
属性将命名插槽应用于元素。有时你不想创建元素,因此我们必须添加<svelte:fragment>
API¥named slots had to be applied to an element using the
slot
attribute. Sometimes you didn’t want to create an element, so we had to add the<svelte:fragment>
API命名插槽也可以应用于组件,这改变了
let:
指令可用位置的语义(即使在今天,我们维护人员也常常不知道如何解决它有效)¥named slots could also be applied to a component, which changed the semantics of where
let:
directives are available (even today us maintainers often don’t know which way around it works)Snippet 通过更易读和更清晰地解决了所有这些问题。同时,它们更强大,因为它们允许你定义可以在任何地方渲染的 UI 部分,而不仅仅是将它们作为 props 传递给组件。
¥Snippets solve all of these problems by being much more readable and clear. At the same time they’re more powerful as they allow you to define sections of UI that you can render anywhere, not just passing them as props to a component.
迁移脚本(Migration script)
¥Migration script
现在你应该对之前/之后以及旧语法与新语法的关系有相当好的理解。可能还很明显,这些迁移中的很多都是相当技术性和重复性的 - 你不想手动执行的操作。
¥By now you should have a pretty good understanding of the before/after and how the old syntax relates to the new syntax. It probably also became clear that a lot of these migrations are rather technical and repetitive - something you don’t want to do by hand.
我们也这么想,这就是我们提供迁移脚本来自动补齐大部分迁移的原因。你可以使用 npx sv migrate svelte-5
升级你的项目。这将执行以下操作:
¥We thought the same, which is why we provide a migration script to do most of the migration automatically. You can upgrade your project by using npx sv migrate svelte-5
. This will do the following things:
在你的
package.json
中增加核心依赖¥bump core dependencies in your
package.json
迁移到符文(
let
->$state
等)¥migrate to runes (
let
->$state
etc)迁移到 DOM 元素的事件属性(
on:click
->onclick
)¥migrate to event attributes for DOM elements (
on:click
->onclick
)将插槽创建迁移到渲染标签(
<slot />
->{@render children()}
)¥migrate slot creations to render tags (
<slot />
->{@render children()}
)将插槽使用迁移到片段(
<div slot="x">...</div>
->{#snippet x()}<div>...</div>{/snippet}
)¥migrate slot usages to snippets (
<div slot="x">...</div>
->{#snippet x()}<div>...</div>{/snippet}
)迁移明显的组件创建(
new Component(...)
->mount(Component, ...)
)¥migrate obvious component creations (
new Component(...)
->mount(Component, ...)
)
你还可以通过 Migrate Component to Svelte 5 Syntax
命令在 VS Code 中迁移单个组件,或者通过 Migrate
按钮在我们的 Playground 中迁移单个组件。
¥You can also migrate a single component in VS Code through the Migrate Component to Svelte 5 Syntax
command, or in our Playground through the Migrate
button.
并非所有内容都可以自动迁移,并且某些迁移之后需要手动清理。以下部分更详细地描述了这些。
¥Not everything can be migrated automatically, and some migrations need manual cleanup afterwards. The following sections describe these in more detail.
run
你可能会看到迁移脚本将某些 $:
语句转换为从 svelte/legacy
导入的 run
函数。如果迁移脚本无法可靠地将语句迁移到 $derived
并得出结论这是一个副作用,就会发生这种情况。在某些情况下,这可能是错误的,最好将其更改为使用 $derived
。在其他情况下它可能是正确的,但由于 $:
语句也在服务器上运行,而 $effect
没有运行,因此将其转换为这样是不安全的。相反,run
被用作权宜之计。run
模仿了 $:
的大部分特性,它在服务器上运行一次,并在客户端上作为 $effect.pre
运行($effect.pre
在将更改应用于 DOM 之前运行;你很可能想使用 $effect
)。
¥You may see that the migration script converts some of your $:
statements to a run
function which is imported from svelte/legacy
. This happens if the migration script couldn’t reliably migrate the statement to a $derived
and concluded this is a side effect instead. In some cases this may be wrong and it’s best to change this to use a $derived
instead. In other cases it may be right, but since $:
statements also ran on the server but $effect
does not, it isn’t safe to transform it as such. Instead, run
is used as a stopgap solution. run
mimics most of the characteristics of $:
, in that it runs on the server once, and runs as $effect.pre
on the client ($effect.pre
runs before changes are applied to the DOM; most likely you want to use $effect
instead).
<script>
import { run } from 'svelte/legacy';
run(() => {
$effect(() => {
// some side effect code
})
</script>
事件修饰符(Event modifiers)
¥Event modifiers
事件修饰符不适用于事件属性(例如,你不能执行 onclick|preventDefault={...}
)。因此,在将事件指令迁移到事件属性时,我们需要这些修饰符的函数替换。这些是从 svelte/legacy
导入的,应该迁移出去,例如只使用 event.preventDefault()
。
¥Event modifiers are not applicable to event attributes (e.g. you can’t do onclick|preventDefault={...}
). Therefore, when migrating event directives to event attributes, we need a function-replacement for these modifiers. These are imported from svelte/legacy
, and should be migrated away from in favor of e.g. just using event.preventDefault()
.
<script>
import { preventDefault } from 'svelte/legacy';
</script>
<button
onclick={preventDefault((event) => {
event.preventDefault();
// ...
})}
>
click me
</button>
未自动迁移的内容(Things that are not automigrated)
¥Things that are not automigrated
迁移脚本不会转换 createEventDispatcher
。你需要手动调整这些部分。它不这样做是因为风险太大,因为它可能会导致组件用户的中断,而迁移脚本无法发现这一点。
¥The migration script does not convert createEventDispatcher
. You need to adjust those parts manually. It doesn’t do it because it’s too risky because it could result in breakage for users of the component, which the migration script cannot find out.
迁移脚本不会转换 beforeUpdate/afterUpdate
。它不这样做是因为无法确定代码的实际意图。根据经验,你通常可以使用 $effect.pre
(与 beforeUpdate
同时运行)和 tick
(从 svelte
导入,允许你等到更改应用于 DOM 然后再执行一些工作)的组合。
¥The migration script does not convert beforeUpdate/afterUpdate
. It doesn’t do it because it’s impossible to determine the actual intent of the code. As a rule of thumb you can often go with a combination of $effect.pre
(runs at the same time as beforeUpdate
did) and tick
(imported from svelte
, allows you to wait until changes are applied to the DOM and then do some work).
组件不再是类(Components are no longer classes)
¥Components are no longer classes
在 Svelte 3 和 4 中,组件是类。在 Svelte 5 中,它们是函数,应该以不同的方式实例化。如果你需要手动实例化组件,则应改用 mount
或 hydrate
(从 svelte
导入)。如果你使用 SvelteKit 时看到此错误,请先尝试更新到最新版本的 SvelteKit,该版本增加了对 Svelte 5 的支持。如果你使用的是没有 SvelteKit 的 Svelte,则可能需要调整 main.js
文件(或类似文件):
¥In Svelte 3 and 4, components are classes. In Svelte 5 they are functions and should be instantiated differently. If you need to manually instantiate components, you should use mount
or hydrate
(imported from svelte
) instead. If you see this error using SvelteKit, try updating to the latest version of SvelteKit first, which adds support for Svelte 5. If you’re using Svelte without SvelteKit, you’ll likely have a main.js
file (or similar) which you need to adjust:
import { function mount<Props extends Record<string, any>, Exports extends Record<string, any>>(component: ComponentType<SvelteComponent<Props>> | Component<Props, Exports, any>, options: MountOptions<Props>): Exports
Mounts a component to the given target and returns the exports and potentially the props (if compiled with accessors: true
) of the component.
Transitions will play during the initial render unless the intro
option is set to false
.
mount } from 'svelte';
import type App = SvelteComponent<Record<string, any>, any, any>
const App: LegacyComponentType
App from './App.svelte'
const app = new App({ target: document.getElementById("app") });
const const app: {
$on?(type: string, callback: (e: any) => void): () => void;
$set?(props: Partial<Record<string, any>>): void;
} & Record<string, any>
app = mount<Record<string, any>, {
$on?(type: string, callback: (e: any) => void): () => void;
$set?(props: Partial<Record<string, any>>): void;
} & Record<...>>(component: ComponentType<...> | Component<...>, options: MountOptions<...>): {
...;
} & Record<...>Mounts a component to the given target and returns the exports and potentially the props (if compiled with accessors: true
) of the component.
Transitions will play during the initial render unless the intro
option is set to false
.
const App: LegacyComponentType
App, { target: Document | Element | ShadowRoot
Target element where the component will be mounted.
var document: Document
document.Document.getElementById(elementId: string): HTMLElement | null
Returns a reference to the first object with the specified value of the ID attribute.
const app: {
$on?(type: string, callback: (e: any) => void): () => void;
$set?(props: Partial<Record<string, any>>): void;
} & Record<string, any>
app;mount
和 hydrate
具有完全相同的 API。不同之处在于 hydrate
将在其目标内拾取 Svelte 的服务器渲染 HTML 并对其进行水化。两者都返回一个对象,其中包含组件的导出和潜在的属性访问器(如果使用 accessors: true
编译)。它们不附带你可能从类组件 API 中了解的 $on
、$set
和 $destroy
方法。这些是它的替代品:
¥mount
and hydrate
have the exact same API. The difference is that hydrate
will pick up the Svelte’s server-rendered HTML inside its target and hydrate it. Both return an object with the exports of the component and potentially property accessors (if compiled with accessors: true
). They do not come with the $on
, $set
and $destroy
methods you may know from the class component API. These are its replacements:
对于 $on
,不是监听事件,而是通过选项参数上的 events
属性传递它们。
¥For $on
, instead of listening to events, pass them via the events
property on the options argument.
import { function mount<Props extends Record<string, any>, Exports extends Record<string, any>>(component: ComponentType<SvelteComponent<Props>> | Component<Props, Exports, any>, options: MountOptions<Props>): Exports
Mounts a component to the given target and returns the exports and potentially the props (if compiled with accessors: true
) of the component.
Transitions will play during the initial render unless the intro
option is set to false
.
mount } from 'svelte';
import type App = SvelteComponent<Record<string, any>, any, any>
const App: LegacyComponentType
App from './App.svelte'
const app = new App({ target: document.getElementById("app") });
app.$on('event', callback);
const const app: {
$on?(type: string, callback: (e: any) => void): () => void;
$set?(props: Partial<Record<string, any>>): void;
} & Record<string, any>
app = mount<Record<string, any>, {
$on?(type: string, callback: (e: any) => void): () => void;
$set?(props: Partial<Record<string, any>>): void;
} & Record<...>>(component: ComponentType<...> | Component<...>, options: MountOptions<...>): {
...;
} & Record<...>Mounts a component to the given target and returns the exports and potentially the props (if compiled with accessors: true
) of the component.
Transitions will play during the initial render unless the intro
option is set to false
.
const App: LegacyComponentType
App, { target: Document | Element | ShadowRoot
Target element where the component will be mounted.
var document: Document
document.Document.getElementById(elementId: string): HTMLElement | null
Returns a reference to the first object with the specified value of the ID attribute.
events?: Record<string, (e: any) => any> | undefined
Allows the specification of events.
event: any
event: callback } });请注意,不鼓励使用
events
— 而是 使用回调¥[!NOTE] Note that using
events
is discouraged — instead, use callbacks
对于 $set
,请改用 $state
来创建反应性属性对象并对其进行操作。如果你在 .js
或 .ts
文件中执行此操作,请调整结尾以包含 .svelte
,即 .svelte.js
或 .svelte.ts
。
¥For $set
, use $state
instead to create a reactive property object and manipulate it. If you’re doing this inside a .js
or .ts
file, adjust the ending to include .svelte
, i.e. .svelte.js
or .svelte.ts
.
import { function mount<Props extends Record<string, any>, Exports extends Record<string, any>>(component: ComponentType<SvelteComponent<Props>> | Component<Props, Exports, any>, options: MountOptions<Props>): Exports
Mounts a component to the given target and returns the exports and potentially the props (if compiled with accessors: true
) of the component.
Transitions will play during the initial render unless the intro
option is set to false
.
mount } from 'svelte';
import type App = SvelteComponent<Record<string, any>, any, any>
const App: LegacyComponentType
App from './App.svelte'
const app = new App({ target: document.getElementById("app"), props: { foo: 'bar' } });
app.$set({ foo: 'baz' });
const const props: {
foo: string;
}
props = function $state<{
foo: string;
}>(initial: {
foo: string;
}): {
foo: string;
} (+1 overload)
namespace $state$state({ foo: string
foo: 'bar' });
const const app: {
$on?(type: string, callback: (e: any) => void): () => void;
$set?(props: Partial<Record<string, any>>): void;
} & Record<string, any>app = mount<Record<string, any>, {
$on?(type: string, callback: (e: any) => void): () => void;
$set?(props: Partial<Record<string, any>>): void;
} & Record<...>>(component: ComponentType<...> | Component<...>, options: MountOptions<...>): {
...;
} & Record<...>Mounts a component to the given target and returns the exports and potentially the props (if compiled with accessors: true
) of the component.
Transitions will play during the initial render unless the intro
option is set to false
.
const App: LegacyComponentType
App, { target: Document | Element | ShadowRoot
Target element where the component will be mounted.
var document: Document
document.Document.getElementById(elementId: string): HTMLElement | null
Returns a reference to the first object with the specified value of the ID attribute.
props?: Record<string, any> | undefined
Component properties.
const props: {
foo: string;
}props.foo: string
foo = 'baz';对于 $destroy
,请改用 unmount
。
¥For $destroy
, use unmount
instead.
import { function mount<Props extends Record<string, any>, Exports extends Record<string, any>>(component: ComponentType<SvelteComponent<Props>> | Component<Props, Exports, any>, options: MountOptions<Props>): Exports
Mounts a component to the given target and returns the exports and potentially the props (if compiled with accessors: true
) of the component.
Transitions will play during the initial render unless the intro
option is set to false
.
mount, function unmount(component: Record<string, any>, options?: {
outro?: boolean;
} | undefined): Promise<void>
Unmounts a component that was previously mounted using mount
or hydrate
.
Since 5.13.0, if options.outro
is true
, transitions will play before the component is removed from the DOM.
Returns a Promise
that resolves after transitions have completed if options.outro
is true, or immediately otherwise (prior to 5.13.0, returns void
).
import { mount, unmount } from 'svelte';
import App from './App.svelte';
const app = mount(App, { target: document.body });
// later...
unmount(app, { outro: true });
type App = SvelteComponent<Record<string, any>, any, any>
const App: LegacyComponentType
App from './App.svelte'
const app = new App({ target: document.getElementById("app"), props: { foo: 'bar' } });
app.$destroy();
const const app: {
$on?(type: string, callback: (e: any) => void): () => void;
$set?(props: Partial<Record<string, any>>): void;
} & Record<string, any>app = mount<Record<string, any>, {
$on?(type: string, callback: (e: any) => void): () => void;
$set?(props: Partial<Record<string, any>>): void;
} & Record<...>>(component: ComponentType<...> | Component<...>, options: MountOptions<...>): {
...;
} & Record<...>Mounts a component to the given target and returns the exports and potentially the props (if compiled with accessors: true
) of the component.
Transitions will play during the initial render unless the intro
option is set to false
.
const App: LegacyComponentType
App, { target: Document | Element | ShadowRoot
Target element where the component will be mounted.
var document: Document
document.Document.getElementById(elementId: string): HTMLElement | null
Returns a reference to the first object with the specified value of the ID attribute.
function unmount(component: Record<string, any>, options?: {
outro?: boolean;
} | undefined): Promise<void>Unmounts a component that was previously mounted using mount
or hydrate
.
Since 5.13.0, if options.outro
is true
, transitions will play before the component is removed from the DOM.
Returns a Promise
that resolves after transitions have completed if options.outro
is true, or immediately otherwise (prior to 5.13.0, returns void
).
import { mount, unmount } from 'svelte';
import App from './App.svelte';
const app = mount(App, { target: document.body });
// later...
unmount(app, { outro: true });
const app: {
$on?(type: string, callback: (e: any) => void): () => void;
$set?(props: Partial<Record<string, any>>): void;
} & Record<string, any>app);作为权宜之计,你还可以使用 createClassComponent
或 asClassComponent
(从 svelte/legacy
导入)来在实例化后保持与 Svelte 4 相同的 API。
¥As a stop-gap-solution, you can also use createClassComponent
or asClassComponent
(imported from svelte/legacy
) instead to keep the same API known from Svelte 4 after instantiating.
import { function createClassComponent<Props extends Record<string, any>, Exports extends Record<string, any>, Events extends Record<string, any>, Slots extends Record<string, any>>(options: ComponentConstructorOptions<Props> & {
component: ComponentType<SvelteComponent<Props, Events, Slots>> | Component<Props>;
}): SvelteComponent<Props, Events, Slots> & Exports
Takes the same options as a Svelte 4 component and the component function and returns a Svelte 4 compatible component.
type App = SvelteComponent<Record<string, any>, any, any>
const App: LegacyComponentType
App from './App.svelte'
const app = new App({ target: document.getElementById("app") });
const const app: SvelteComponent<Record<string, any>, any, any> & Record<string, any>
app = createClassComponent<Record<string, any>, Record<string, any>, any, any>(options: ComponentConstructorOptions<Record<string, any>> & {
component: Component<...> | ComponentType<...>;
}): SvelteComponent<...> & Record<...>Takes the same options as a Svelte 4 component and the component function and returns a Svelte 4 compatible component.
component: Component<Record<string, any>, {}, string> | ComponentType<SvelteComponent<Record<string, any>, any, any>>
component: const App: LegacyComponentType
App, ComponentConstructorOptions<Props extends Record<string, any> = Record<string, any>>.target: Document | Element | ShadowRoot
target: var document: Document
document.Document.getElementById(elementId: string): HTMLElement | null
Returns a reference to the first object with the specified value of the ID attribute.
const app: SvelteComponent<Record<string, any>, any, any> & Record<string, any>
app;如果此组件不受你的控制,你可以使用 compatibility.componentApi
编译器选项自动应用向后兼容性,这意味着使用 new Component(...)
的代码无需调整即可继续工作(请注意,这会给每个组件增加一些开销)。这还将为你通过 bind:this
获得的所有组件实例添加 $set
和 $on
方法。
¥If this component is not under your control, you can use the compatibility.componentApi
compiler option for auto-applied backwards compatibility, which means code using new Component(...)
keeps working without adjustments (note that this adds a bit of overhead to each component). This will also add $set
and $on
methods for all component instances you get through bind:this
.
/// svelte.config.js
export default {
compilerOptions: {
compatibility: {
componentApi: number;
};
}
compilerOptions: {
compatibility: {
componentApi: number;
}
compatibility: {
componentApi: number
componentApi: 4
}
}
};
请注意,mount
和 hydrate
不是同步的,因此在函数返回时不会调用 onMount
之类的东西,并且待处理的 promise 块尚未渲染(因为 #await
等待微任务以等待可能立即解决的 promise)。如果你需要该保证,请在调用 mount/hydrate
后调用 flushSync
(从 'svelte'
导入)。
¥Note that mount
and hydrate
are not synchronous, so things like onMount
won’t have been called by the time the function returns and the pending block of promises will not have been rendered yet (because #await
waits a microtask to wait for a potentially immediately-resolved promise). If you need that guarantee, call flushSync
(import from 'svelte'
) after calling mount/hydrate
.
服务器 API 更改(Server API changes)
¥Server API changes
同样,在为服务器端渲染编译时,组件不再具有 render
方法。相反,将函数从 svelte/server
传递给 render
:
¥Similarly, components no longer have a render
method when compiled for server side rendering. Instead, pass the function to render
from svelte/server
:
import { function render<Comp extends SvelteComponent<any> | Component<any>, Props extends ComponentProps<Comp> = ComponentProps<Comp>>(...args: {} extends Props ? [component: Comp extends SvelteComponent<any> ? ComponentType<Comp> : Comp, options?: {
props?: Omit<Props, "$$slots" | "$$events">;
context?: Map<any, any>;
}] : [component: Comp extends SvelteComponent<any> ? ComponentType<Comp> : Comp, options: {
props: Omit<Props, "$$slots" | "$$events">;
context?: Map<any, any>;
}]): RenderOutput
Only available on the server and when compiling with the server
option.
Takes a component and returns an object with body
and head
properties on it, which you can use to populate the HTML when server-rendering your app.
type App = SvelteComponent<Record<string, any>, any, any>
const App: LegacyComponentType
App from './App.svelte';
const { html, head } = App.render({ props: { message: 'hello' }});
const { const html: string
html, const head: string
HTML that goes into the <head>
render<SvelteComponent<Record<string, any>, any, any>, Record<string, any>>(component: ComponentType<SvelteComponent<Record<string, any>, any, any>>, options?: {
...;
} | undefined): RenderOutputOnly available on the server and when compiling with the server
option.
Takes a component and returns an object with body
and head
properties on it, which you can use to populate the HTML when server-rendering your app.
const App: LegacyComponentType
App, { props?: Omit<Record<string, any>, "$$slots" | "$$events"> | undefined
props: { message: string
message: 'hello' }});在 Svelte 4 中,将组件渲染为字符串还会返回所有组件的 CSS。在 Svelte 5 中,默认情况下不再是这种情况,因为大多数时候你都在使用以其他方式处理它的工具链(如 SvelteKit)。如果你需要从 render
返回 CSS,你可以将 css
编译器选项设置为 'injected'
,它会将 <style>
元素添加到 head
。
¥In Svelte 4, rendering a component to a string also returned the CSS of all components. In Svelte 5, this is no longer the case by default because most of the time you’re using a tooling chain that takes care of it in other ways (like SvelteKit). If you need CSS to be returned from render
, you can set the css
compiler option to 'injected'
and it will add <style>
elements to the head
.
组件类型更改(Component typing changes)
¥Component typing changes
从类到函数的变化也反映在类型中:SvelteComponent
是 Svelte 4 中的基类,已弃用,取而代之的是新的 Component
类型,该类型定义了 Svelte 组件的函数形状。要在 d.ts
文件中手动定义组件形状:
¥The change from classes towards functions is also reflected in the typings: SvelteComponent
, the base class from Svelte 4, is deprecated in favour of the new Component
type which defines the function shape of a Svelte component. To manually define a component shape in a d.ts
file:
import type { interface Component<Props extends Record<string, any> = {}, Exports extends Record<string, any> = {}, Bindings extends keyof Props | "" = string>
Can be used to create strongly typed Svelte components.
Example:
You have component library on npm called component-library
, from which
you export a component called MyComponent
. For Svelte+TypeScript users,
you want to provide typings. Therefore you create a index.d.ts
:
import type { Component } from 'svelte';
export declare const MyComponent: Component<{ foo: string }> {}
Typing this makes it possible for IDEs like VS Code with the Svelte extension
to provide intellisense and to use the component like this in a Svelte file
with TypeScript:
<script lang="ts">
import { MyComponent } from "component-library";
</script>
<MyComponent foo={'bar'} />
Component } from 'svelte';
export declare const const MyComponent: Component<{
foo: string;
}, {}, string>
MyComponent: interface Component<Props extends Record<string, any> = {}, Exports extends Record<string, any> = {}, Bindings extends keyof Props | "" = string>
Can be used to create strongly typed Svelte components.
Example:
You have component library on npm called component-library
, from which
you export a component called MyComponent
. For Svelte+TypeScript users,
you want to provide typings. Therefore you create a index.d.ts
:
import type { Component } from 'svelte';
export declare const MyComponent: Component<{ foo: string }> {}
Typing this makes it possible for IDEs like VS Code with the Svelte extension
to provide intellisense and to use the component like this in a Svelte file
with TypeScript:
<script lang="ts">
import { MyComponent } from "component-library";
</script>
<MyComponent foo={'bar'} />
Component<{
foo: string
foo: string;
}>;
要声明需要某种类型的组件:
¥To declare that a component of a certain type is required:
import { import ComponentA
ComponentA, import ComponentB
ComponentB } from 'component-library';
import type { SvelteComponent } from 'svelte';
import type { interface Component<Props extends Record<string, any> = {}, Exports extends Record<string, any> = {}, Bindings extends keyof Props | "" = string>
Can be used to create strongly typed Svelte components.
Example:
You have component library on npm called component-library
, from which
you export a component called MyComponent
. For Svelte+TypeScript users,
you want to provide typings. Therefore you create a index.d.ts
:
import type { Component } from 'svelte';
export declare const MyComponent: Component<{ foo: string }> {}
Typing this makes it possible for IDEs like VS Code with the Svelte extension
to provide intellisense and to use the component like this in a Svelte file
with TypeScript:
<script lang="ts">
import { MyComponent } from "component-library";
</script>
<MyComponent foo={'bar'} />
Component } from 'svelte';
let C: typeof SvelteComponent<{ foo: string }> = $state(
let let C: Component<{
foo: string;
}, {}, string>
C: interface Component<Props extends Record<string, any> = {}, Exports extends Record<string, any> = {}, Bindings extends keyof Props | "" = string>
Can be used to create strongly typed Svelte components.
Example:
You have component library on npm called component-library
, from which
MyComponent
. For Svelte+TypeScript users,
you want to provide typings. Therefore you create a index.d.ts
:
import type { Component } from 'svelte';
export declare const MyComponent: Component<{ foo: string }> {}
Typing this makes it possible for IDEs like VS Code with the Svelte extension
to provide intellisense and to use the component like this in a Svelte file with TypeScript:<script lang="ts">
import { MyComponent } from "component-library";
</script>
<MyComponent foo={'bar'} />
foo: string
foo: string }> = function $state<any>(initial: any): any (+1 overload)
namespace $state$state(
var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math.random(): number
Returns a pseudorandom number between 0 and 1.
import ComponentA
ComponentA : import ComponentB
ComponentB
);两种工具类型 ComponentEvents
和 ComponentType
也已弃用。ComponentEvents
已过时,因为事件现在被定义为回调 props,而 ComponentType
已过时,因为新的 Component
类型已经是组件类型(即 ComponentType<SvelteComponent<{ prop: string }>>
相当于 Component<{ prop: string }>
)。
¥The two utility types ComponentEvents
and ComponentType
are also deprecated. ComponentEvents
is obsolete because events are defined as callback props now, and ComponentType
is obsolete because the new Component
type is the component type already (i.e. ComponentType<SvelteComponent<{ prop: string }>>
is equivalent to Component<{ prop: string }>
).
bind:this 更改(bind:this changes)
¥bind:this changes
由于组件不再是类,使用 bind:this
不再返回带有 $set
、$on
和 $destroy
方法的类实例。它仅返回实例导出(export function/const
),如果你使用 accessors
选项,则返回每个属性的 getter/setter 对。
¥Because components are no longer classes, using bind:this
no longer returns a class instance with $set
, $on
and $destroy
methods on it. It only returns the instance exports (export function/const
) and, if you’re using the accessors
option, a getter/setter-pair for each property.
<svelte:component> 不再需要(<svelte:component> is no longer necessary)
¥<svelte:component>
is no longer necessary
在 Svelte 4 中,组件是静态的 - 如果你渲染 <Thing>
,并且 Thing
的值发生变化,则 什么都没有发生。要使其动态化,你必须使用 <svelte:component>
。
¥In Svelte 4, components are static — if you render <Thing>
, and the value of Thing
changes, nothing happens. To make it dynamic you had to use <svelte:component>
.
这在 Svelte 5 中不再适用:
¥This is no longer true in Svelte 5:
<script>
import A from './A.svelte';
import B from './B.svelte';
let Thing = $state();
</script>
<select bind:value={Thing}>
<option value={A}>A</option>
<option value={B}>B</option>
</select>
<!-- these are equivalent -->
<Thing />
<svelte:component this={Thing} />
在迁移时,请记住,除非使用点符号,否则组件的名称应大写(Thing
)以将其与元素区分开来。
¥While migrating, keep in mind that your component’s name should be capitalized (Thing
) to distinguish it from elements, unless using dot notation.
点符号表示组件(Dot notation indicates a component)
¥Dot notation indicates a component
在 Svelte 4 中,<foo.bar>
将创建一个带有标签名称 "foo.bar"
的元素。在 Svelte 5 中,foo.bar
被视为组件。这在 each
块内特别有用:
¥In Svelte 4, <foo.bar>
would create an element with a tag name of "foo.bar"
. In Svelte 5, foo.bar
is treated as a component instead. This is particularly useful inside each
blocks:
{#each items as item}
<item.component {...item.props} />
{/each}
空格处理已更改(Whitespace handling changed)
¥Whitespace handling changed
以前,Svelte 使用了一种非常复杂的算法来确定是否应保留空格。Svelte 5 简化了这一过程,使开发者更容易推断。规则如下:
¥Previously, Svelte employed a very complicated algorithm to determine if whitespace should be kept or not. Svelte 5 simplifies this which makes it easier to reason about as a developer. The rules are:
节点之间的空格被折叠为一个空格
¥Whitespace between nodes is collapsed to one whitespace
标签开头和结尾的空格被完全删除
¥Whitespace at the start and end of a tag is removed completely
某些例外情况适用,例如保留
pre
标签内的空格¥Certain exceptions apply such as keeping whitespace inside
pre
tags
与以前一样,你可以通过在编译器设置中或在 <svelte:options>
中按组件设置 preserveWhitespace
选项来禁用空格修剪。
¥As before, you can disable whitespace trimming by setting the preserveWhitespace
option in your compiler settings or on a per-component basis in <svelte:options>
.
现代浏览器必需(Modern browser required)
¥Modern browser required
Svelte 5 需要现代浏览器(换句话说,不是 Internet Explorer),原因如下:
¥Svelte 5 requires a modern browser (in other words, not Internet Explorer) for various reasons:
仅 JavaScript
¥it uses
Proxies
具有
clientWidth
/clientHeight
/offsetWidth
/offsetHeight
绑定的元素使用ResizeObserver
而不是复杂的<iframe>
黑客¥elements with
clientWidth
/clientHeight
/offsetWidth
/offsetHeight
bindings use aResizeObserver
rather than a convoluted<iframe>
hack<input type="range" bind:value={...} />
仅使用input
事件监听器,而不是同时监听change
事件作为后备¥
<input type="range" bind:value={...} />
only uses aninput
event listener, rather than also listening forchange
events as a fallback
legacy
编译器选项不再存在,它生成了更庞大但更适合 IE 的代码。
¥The legacy
compiler option, which generated bulkier but IE-friendly code, no longer exists.
编译器选项的更改(Changes to compiler options)
¥Changes to compiler options
false
/true
(之前已弃用)和"none"
值已从css
选项中删除,不再作为有效值¥The
false
/true
(already deprecated previously) and the"none"
values were removed as valid values from thecss
optionlegacy
选项已重新利用¥The
legacy
option was repurposedhydratable
选项已被删除。Svelte 组件现在始终是可水合的¥The
hydratable
option has been removed. Svelte components are always hydratable nowenableSourcemap
选项已被删除。现在始终会生成源映射,工具可以选择忽略它¥The
enableSourcemap
option has been removed. Source maps are always generated now, tooling can choose to ignore ittag
选项已被删除。改用组件内部的<svelte:options customElement="tag-name" />
¥The
tag
option was removed. Use<svelte:options customElement="tag-name" />
inside the component insteadloopGuardTimeout
、format
、sveltePath
、errorMode
和varsReport
选项已被删除¥The
loopGuardTimeout
,format
,sveltePath
,errorMode
andvarsReport
options were removed
children 属性是保留的(The children prop is reserved)
¥The children
prop is reserved
组件标签内的内容成为名为 children
的片段属性。你不能有同名的单独 prop。
¥Content inside component tags becomes a snippet prop called children
. You cannot have a separate prop by that name.
符文模式下的重大更改(Breaking changes in runes mode)
¥Breaking changes in runes mode
某些重大更改仅在你的组件处于符文模式时才适用。
¥Some breaking changes only apply once your component is in runes mode.
不允许绑定到组件导出(Bindings to component exports are not allowed)
¥Bindings to component exports are not allowed
无法直接绑定来自符文模式组件的导出。例如,在组件 A
中拥有 export const foo = ...
然后执行 <A bind:foo />
会导致错误。改用 bind:this
— <A bind:this={a} />
— 并以 a.foo
的身份访问导出。此更改使事情更容易推断,因为它强制在 props 和导出之间进行明确分离。
¥Exports from runes mode components cannot be bound to directly. For example, having export const foo = ...
in component A
and then doing <A bind:foo />
causes an error. Use bind:this
instead — <A bind:this={a} />
— and access the export as a.foo
. This change makes things easier to reason about, as it enforces a clear separation between props and exports.
需要使用 $bindable() 明确定义绑定(Bindings need to be explicitly defined using $bindable())
¥Bindings need to be explicitly defined using $bindable()
在 Svelte 4 语法中,每个属性(通过 export let
声明)都是可绑定的,这意味着你可以 bind:
到它。在符文模式下,默认情况下属性不可绑定:你需要用 $bindable
符文表示可绑定的属性。
¥In Svelte 4 syntax, every property (declared via export let
) is bindable, meaning you can bind:
to it. In runes mode, properties are not bindable by default: you need to denote bindable props with the $bindable
rune.
如果可绑定属性具有默认值(例如 let { foo = $bindable('bar') } = $props();
),则需要将非 undefined
值传递给该属性(如果你要绑定到它)。这可以防止出现模棱两可的行为 - 父级和子级必须具有相同的值 - 并带来更好的性能(在 Svelte 4 中,默认值被反映回父级,导致浪费额外的渲染周期)。
¥If a bindable property has a default value (e.g. let { foo = $bindable('bar') } = $props();
), you need to pass a non-undefined
value to that property if you’re binding to it. This prevents ambiguous behavior — the parent and child must have the same value — and results in better performance (in Svelte 4, the default value was reflected back to the parent, resulting in wasteful additional render cycles).
accessors 选项被忽略(accessors option is ignored)
¥accessors
option is ignored
将 accessors
选项设置为 true
可使组件的属性在组件实例上直接访问。在符文模式下,组件实例上的属性永远无法访问。如果你需要公开它们,则可以改用组件导出。
¥Setting the accessors
option to true
makes properties of a component directly accessible on the component instance. In runes mode, properties are never accessible on the component instance. You can use component exports instead if you need to expose them.
immutable 选项被忽略(immutable option is ignored)
¥immutable
option is ignored
在符文模式下设置 immutable
选项无效。这个概念被 $state
及其变体的工作方式所取代。
¥Setting the immutable
option has no effect in runes mode. This concept is replaced by how $state
and its variations work.
类不再是 “auto-reactive”(Classes are no longer “auto-reactive”)
¥Classes are no longer “auto-reactive”
在 Svelte 4 中,执行以下操作会触发反应性:
¥In Svelte 4, doing the following triggered reactivity:
<script>
let foo = new Foo();
</script>
<button on:click={() => (foo.value = 1)}>{foo.value}</button
>
这是因为 Svelte 编译器将对 foo.value
的分配视为更新引用 foo
的任何内容的指令。在 Svelte 5 中,反应性是在运行时而不是编译时确定的,因此你应该将 value
定义为 Foo
类上的反应性 $state
字段。用 $state(...)
封装 new Foo()
不会产生任何效果 - 只有原始对象和数组才会变得具有深度响应性。
¥This is because the Svelte compiler treated the assignment to foo.value
as an instruction to update anything that referenced foo
. In Svelte 5, reactivity is determined at runtime rather than compile time, so you should define value
as a reactive $state
field on the Foo
class. Wrapping new Foo()
with $state(...)
will have no effect — only vanilla objects and arrays are made deeply reactive.
触摸和滚轮事件是被动的(Touch and wheel events are passive)
¥Touch and wheel events are passive
使用 onwheel
、onmousewheel
、ontouchstart
和 ontouchmove
事件属性时,处理程序为 passive,以与浏览器默认值保持一致。通过允许浏览器立即滚动文档,而不是等待查看事件处理程序是否调用 event.preventDefault()
,这极大地提高了响应能力。
¥When using onwheel
, onmousewheel
, ontouchstart
and ontouchmove
event attributes, the handlers are passive to align with browser defaults. This greatly improves responsiveness by allowing the browser to scroll the document immediately, rather than waiting to see if the event handler calls event.preventDefault()
.
在极少数情况下,你需要防止这些事件默认值,你应该改用 on
(例如在操作内部)。
¥In the very rare cases that you need to prevent these event defaults, you should use on
instead (for example inside an action).
属性/prop 语法更严格(Attribute/prop syntax is stricter)
¥Attribute/prop syntax is stricter
在 Svelte 4 中,复杂的属性值不需要引用:
¥In Svelte 4, complex attribute values needn’t be quoted:
<Component prop=this{is}valid />
这是一个被动手段。在符文模式下,如果你想连接内容,则必须将值括在引号中:
¥This is a footgun. In runes mode, if you want to concatenate stuff you must wrap the value in quotes:
<Component prop="this{is}valid" />
请注意,如果你有一个用引号括起来的表达式,例如 Svelte 6 中的 answer="{42}"
— Svelte 5 也会发出警告,这将导致值转换为字符串,而不是作为数字传递。
¥Note that Svelte 5 will also warn if you have a single expression wrapped in quotes, like answer="{42}"
— in Svelte 6, that will cause the value to be converted to a string, rather than passed as a number.
HTML 结构更严格(HTML structure is stricter)
¥HTML structure is stricter
在 Svelte 4 中,你可以编写 HTML 代码,该代码将在服务器端渲染时由浏览器修复。例如,你可以这样写...
¥In Svelte 4, you were allowed to write HTML code that would be repaired by the browser when server side rendering it. For example you could write this...
<table>
<tr>
<td>hi</td>
</tr>
</table>
...浏览器会自动插入 <tbody>
元素:
¥... and the browser would auto-insert a <tbody>
element:
<table>
<tbody>
<tr>
<td>hi</td>
</tr>
</tbody>
</table>
Svelte 5 对 HTML 结构的要求更严格,如果浏览器要修复 DOM,它会抛出编译器错误。
¥Svelte 5 is more strict about the HTML structure and will throw a compiler error in cases where the browser would repair the DOM.
其他重大更改(Other breaking changes)
¥Other breaking changes
更严格的 @const 分配验证(Stricter @const assignment validation)
¥Stricter @const
assignment validation
不再允许对 @const
声明的解构部分进行赋值。这是一个疏忽,它曾经被允许。
¥Assignments to destructured parts of a @const
declaration are no longer allowed. It was an oversight that this was ever allowed.
:is(...) 和 :where(...) 有作用域(:is(...) and :where(...) are scoped)
¥:is(...) and :where(...) are scoped
以前,Svelte 不会分析 :is(...)
和 :where(...)
内的选择器,而是将它们有效地视为全局选择器。Svelte 5 在当前组件的上下文中分析它们。因此,如果某些选择器依赖于这种处理,它们现在可能会被视为未使用。要修复此问题,请在 :is(...)/:where(...)
选择器中使用 :global(...)
。
¥Previously, Svelte did not analyse selectors inside :is(...)
and :where(...)
, effectively treating them as global. Svelte 5 analyses them in the context of the current component. As such, some selectors may now be treated as unused if they were relying on this treatment. To fix this, use :global(...)
inside the :is(...)/:where(...)
selectors.
使用 Tailwind 的 @apply
指令时,添加 :global
选择器以保留使用 Tailwind 生成的 :is(...)
选择器的规则:
¥When using Tailwind’s @apply
directive, add a :global
selector to preserve rules that use Tailwind-generated :is(...)
selectors:
main :global {
@apply bg-blue-100 dark:bg-blue-900;
}
CSS 哈希位置不再确定(CSS hash position no longer deterministic)
¥CSS hash position no longer deterministic
以前,Svelte 总是最后插入 CSS 哈希。这在 Svelte 5 中不再有保证。这只有在你 有非常奇怪的 css 选择器 时才会中断。
¥Previously Svelte would always insert the CSS hash last. This is no longer guaranteed in Svelte 5. This is only breaking if you have very weird css selectors.
作用域 CSS 使用 :where(...)(Scoped CSS uses :where(...))
¥Scoped CSS uses :where(...)
为避免由不可预测的特殊性变化引起的问题,范围 CSS 选择器现在使用 :where(.svelte-xyz123)
选择器修饰符和 .svelte-xyz123
(其中 xyz123
与以前一样,是 <style>
内容的哈希值)。你可以阅读更多详细信息 此处。
¥To avoid issues caused by unpredictable specificity changes, scoped CSS selectors now use :where(.svelte-xyz123)
selector modifiers alongside .svelte-xyz123
(where xyz123
is, as previously, a hash of the <style>
contents). You can read more detail here.
如果你需要支持未实现 :where
的古老浏览器,你可以手动更改发出的 CSS,但代价是不可预测的特殊性变化:
¥In the event that you need to support ancient browsers that don’t implement :where
, you can manually alter the emitted CSS, at the cost of unpredictable specificity changes:
css = css.replace(/:where\((.+?)\)/, '$1');
错误/警告代码已重命名(Error/warning codes have been renamed)
¥Error/warning codes have been renamed
错误和警告代码已重命名。以前他们使用破折号来分隔单词,现在他们使用下划线(例如 foo-bar 变成 foo_bar)。此外,一些代码已略微改写。
¥Error and warning codes have been renamed. Previously they used dashes to separate the words, they now use underscores (e.g. foo-bar becomes foo_bar). Additionally, a handful of codes have been reworded slightly.
减少命名空间数量(Reduced number of namespaces)
¥Reduced number of namespaces
你可以传递给编译器选项 namespace
的有效命名空间数量已减少到 html
(默认值)、mathml
和 svg
。
¥The number of valid namespaces you can pass to the compiler option namespace
has been reduced to html
(the default), mathml
and svg
.
foreign
命名空间仅适用于 Svelte Native,我们计划在 5.x 次要版本中以不同的方式支持它。
¥The foreign
namespace was only useful for Svelte Native, which we’re planning to support differently in a 5.x minor.
beforeUpdate/afterUpdate 更改(beforeUpdate/afterUpdate changes)
¥beforeUpdate/afterUpdate changes
如果 beforeUpdate
修改了模板中引用的变量,则在初始渲染时不再运行两次。
¥beforeUpdate
no longer runs twice on initial render if it modifies a variable referenced in the template.
父组件中的 afterUpdate
回调现在将在任何子组件中的 afterUpdate
回调之后运行。
¥afterUpdate
callbacks in a parent component will now run after afterUpdate
callbacks in any child components.
当组件包含 <slot>
且其内容更新时,beforeUpdate/afterUpdate
不再运行。
¥beforeUpdate/afterUpdate
no longer run when the component contains a <slot>
and its content is updated.
在符文模式下,这两个函数都是不允许的 - 请改用 $effect.pre(...)
和 $effect(...)
。
¥Both functions are disallowed in runes mode — use $effect.pre(...)
and $effect(...)
instead.
contenteditable 行为更改(contenteditable behavior change)
¥contenteditable
behavior change
如果你有一个具有相应绑定和其中的反应值(例如:<div contenteditable=true bind:textContent>count is {count}</div>
)的 contenteditable
节点,则 contenteditable 中的值将不会通过对 count
的更新进行更新,因为绑定会立即完全控制内容,并且只能通过它进行更新。
¥If you have a contenteditable
node with a corresponding binding and a reactive value inside it (example: <div contenteditable=true bind:textContent>count is {count}</div>
), then the value inside the contenteditable will not be updated by updates to count
because the binding takes full control over the content immediately and it should only be updated through it.
oneventname 属性不再接受字符串值(oneventname attributes no longer accept string values)
¥oneventname
attributes no longer accept string values
在 Svelte 4 中,可以将 HTML 元素上的事件属性指定为字符串:
¥In Svelte 4, it was possible to specify event attributes on HTML elements as a string:
<button onclick="alert('hello')">...</button>
不建议这样做,在 Svelte 5 中不再可能,其中 onclick
等属性取代 on:click
作为添加事件处理程序的机制。
¥This is not recommended, and is no longer possible in Svelte 5, where properties like onclick
replace on:click
as the mechanism for adding event handlers.
null 和 undefined 变为空字符串(null and undefined become the empty string)
¥null
and undefined
become the empty string
在 Svelte 4 中,null
和 undefined
被打印为相应的字符串。在 100 个案例中有 99 个你希望它变成空字符串,这也是大多数其他框架所做的。因此,在 Svelte 5 中,null
和 undefined
变为空字符串。
¥In Svelte 4, null
and undefined
were printed as the corresponding string. In 99 out of 100 cases you want this to become the empty string instead, which is also what most other frameworks out there do. Therefore, in Svelte 5, null
and undefined
become the empty string.
bind:files 值只能是 null、undefined 或 FileList(bind:files values can only be null, undefined or FileList)
¥bind:files
values can only be null
, undefined
or FileList
bind:files
现在是双向绑定。因此,设置值时,它需要为假值(null
或 undefined
)或类型为 FileList
。
¥bind:files
is now a two-way binding. As such, when setting a value, it needs to be either falsy (null
or undefined
) or of type FileList
.
绑定现在对表单重置做出反应(Bindings now react to form resets)
¥Bindings now react to form resets
以前,绑定没有考虑表单的 reset
事件,因此值可能与 DOM 不同步。Svelte 5 通过在文档上放置 reset
监听器并在必要时调用绑定来解决这个问题。
¥Previously, bindings did not take into account reset
event of forms, and therefore values could get out of sync with the DOM. Svelte 5 fixes this by placing a reset
listener on the document and invoking bindings where necessary.
walk 否不再导出(walk no longer exported)
¥walk
no longer exported
为方便起见,svelte/compiler
从 estree-walker
重新导出了 walk
。这在 Svelte 5 中不再适用,如果需要,请直接从该包中导入它。
¥svelte/compiler
reexported walk
from estree-walker
for convenience. This is no longer true in Svelte 5, import it directly from that package instead in case you need it.
svelte:options 内的内容被禁止(Content inside svelte:options is forbidden)
¥Content inside svelte:options
is forbidden
在 Svelte 4 中,你可以在 <svelte:options />
标签内包含内容。它被忽略了,但你可以在里面写点什么。在 Svelte 5 中,该标签内的内容是编译器错误。
¥In Svelte 4 you could have content inside a <svelte:options />
tag. It was ignored, but you could write something in there. In Svelte 5, content inside that tag is a compiler error.
声明性影子根中的 <slot> 元素被保留(<slot> elements in declarative shadow roots are preserved)
¥<slot>
elements in declarative shadow roots are preserved
Svelte 4 用自己的插槽版本替换了所有地方的 <slot />
标签。如果它们是 <template shadowrootmode="...">
元素的子元素,Svelte 5 会保留它们。
¥Svelte 4 replaced the <slot />
tag in all places with its own version of slots. Svelte 5 preserves them in the case they are a child of a <template shadowrootmode="...">
element.
<svelte:element> 标签必须是表达式(<svelte:element> tag must be an expression)
¥<svelte:element>
tag must be an expression
在 Svelte 4 中,<svelte:element this="div">
是有效代码。这没有什么意义 - 你应该只做 <div>
。在极少数情况下,如果你出于某种原因确实需要使用字面量值,你可以这样做:
¥In Svelte 4, <svelte:element this="div">
is valid code. This makes little sense — you should just do <div>
. In the vanishingly rare case that you do need to use a literal value for some reason, you can do this:
<svelte:element this={"div"}>
请注意,虽然 Svelte 4 会将 <svelte:element this="input">
(例如)与 <input>
相同地处理,以确定可以应用哪些 bind:
指令,但 Svelte 5 不会。
¥Note that whereas Svelte 4 would treat <svelte:element this="input">
(for example) identically to <input>
for the purposes of determining which bind:
directives could be applied, Svelte 5 does not.
mount 默认播放过渡(mount plays transitions by default)
¥mount
plays transitions by default
用于渲染组件树的 mount
函数默认播放转换,除非 intro
选项设置为 false
。这与旧式类组件不同,后者在手动实例化时默认不播放过渡。
¥The mount
function used to render a component tree plays transitions by default unless the intro
option is set to false
. This is different from legacy class components which, when manually instantiated, didn’t play transitions by default.
<img src={...}> 和 {@html ...} 水合不匹配未修复(<img src={...}> and {@html ...} hydration mismatches are not repaired)
¥<img src={...}>
and {@html ...}
hydration mismatches are not repaired
在 Svelte 4 中,如果 src
属性或 {@html ...}
标签的值在服务器和客户端之间不同(又称水合不匹配),则会修复不匹配。这非常昂贵:设置 src
属性(即使其评估结果相同)会导致重新加载图片和 iframe,并且重新插入大量 HTML 会很慢。
¥In Svelte 4, if the value of a src
attribute or {@html ...}
tag differ between server and client (a.k.a. a hydration mismatch), the mismatch is repaired. This is very costly: setting a src
attribute (even if it evaluates to the same thing) causes images and iframes to be reloaded, and reinserting a large blob of HTML is slow.
由于这些不匹配的情况极为罕见,Svelte 5 假定值保持不变,但在开发过程中,如果值没有改变,则会向你发出警告。要强制更新,你可以执行以下操作:
¥Since these mismatches are extremely rare, Svelte 5 assumes that the values are unchanged, but in development will warn you if they are not. To force an update you can do something like this:
<script>
let { markup, src } = $props();
if (typeof window !== 'undefined') {
// stash the values...
const initial = { markup, src };
// unset them...
markup = src = undefined;
$effect(() => {
// ...and reset after we've mounted
markup = initial.markup;
src = initial.src;
});
}
</script>
{@html markup}
<img {src} />
Hydration 的工作方式不同(Hydration works differently)
¥Hydration works differently
Svelte 5 在服务器端渲染期间使用注释,用于在客户端上实现更强大、更高效的水合。因此,如果你打算对 HTML 输出进行补充,则不应从其中删除注释;如果你手动编写了要由 Svelte 组件补充的 HTML,则需要调整该 HTML 以在正确的位置包含所述注释。
¥Svelte 5 makes use of comments during server side rendering which are used for more robust and efficient hydration on the client. As such, you shouldn’t remove comments from your HTML output if you intend to hydrate it, and if you manually authored HTML to be hydrated by a Svelte component, you need to adjust that HTML to include said comments at the correct positions.
onevent 属性被委托(onevent attributes are delegated)
¥onevent
attributes are delegated
事件属性替换事件指令:用 onclick={handler}
代替 on:click={handler}
。为了向后兼容,on:event
语法仍然受支持,并且行为与 Svelte 4 中相同。但是,某些 onevent
属性是委托的,这意味着你需要注意不要手动停止这些属性上的事件传播,因为它们可能永远不会到达根目录下此事件类型的监听器。
¥Event attributes replace event directives: Instead of on:click={handler}
you write onclick={handler}
. For backwards compatibility the on:event
syntax is still supported and behaves the same as in Svelte 4. Some of the onevent
attributes however are delegated, which means you need to take care to not stop event propagation on those manually, as they then might never reach the listener for this event type at the root.
--style-props 使用不同的元素(--style-props uses a different element)
¥--style-props
uses a different element
Svelte 5 使用额外的 <svelte-css-wrapper>
元素而不是 <div>
来封装使用 CSS 自定义属性的组件。
¥Svelte 5 uses an extra <svelte-css-wrapper>
element instead of a <div>
to wrap the component when using CSS custom properties.