Skip to main content

Svelte 5 migration guide

版本 5 带来了全新的语法和反应性系统。虽然一开始可能看起来不同,但很快你就会发现许多相似之处。本指南详细介绍了这些变化,并展示了如何升级。此外,我们还提供了关于为什么进行这些更改的原因信息。

您不必立即迁移到新语法 - Svelte 5 仍然支持旧版 Svelte 4 语法,并且您可以使用新语法与旧语法组件混合使用,反之亦然。我们预计许多人只需更改几行代码即可升级。此外,还有一个 迁移脚本,可以帮助您自动完成许多这些步骤。

反应性语法更改

Svelte 5 的核心是新的 runes API。Runes 主要是编译器指令,用于告知 Svelte 关于响应性。在语法上,runes 是以美元符号开头的函数。

let → $状态

在 Svelte 4 中,组件顶层的一个 let 声明是隐式响应式的。在 Svelte 5 中,事情变得更加明确:当使用 $state 符文创建变量时,该变量是响应式的。让我们通过将计数器包裹在 $state 中将计数器迁移到符文模式:

<script>
	let count = $state(0);
</script>

无其他更改。count仍然是数字本身,您可以直接读取和写入它,无需像.valuegetCount()这样的包装器。

[!详细信息] 为什么我们在顶层将let设置为隐式响应式效果很好,但这意味着响应式被限制 - 任何其他地方的let声明都不是响应式的。这迫使你在将代码从组件顶层重构以供重用时求助于使用存储。这意味着你必须学习一个完全不同的响应式模型,而且结果往往不那么容易处理。由于 Svelte 5 中的响应式更明确,你可以在组件顶层之外继续使用相同的 API。前往教程了解更多信息。

$: → $derived/$effect

在 Svelte 4 中,组件顶层的一个$:语句可以用来声明一个派生,即完全通过其他状态的计算定义的状态。在 Svelte 5 中,这通过使用$derived符号实现:

<script>
	let count = $state(0);
	$: const double = $derived(count * 2);
</script>

$state一样,其他什么都没变。double仍然是数字本身,你可以直接读取,无需像.valuegetDouble()这样的包装。

A $: 语句也可以用来创建副作用。在 Svelte 5 中,这通过使用 $effect 符文来实现:

<script>
	let count = $state(0);

	$:$effect(() => {
		if (count > 5) {
			alert('Count is too high!');
		}
	});
</script>

请注意,当 $effect 运行时与当 $: 运行时不同。

[!详细信息] 我们为什么要这样做 $: 这是一个很好的简写,并且易于上手:你可以在大多数代码前加上 $:,它就会以某种方式工作。这种直观性也是它的缺点,因为随着代码变得越来越复杂,它就不那么容易推理了。代码的意图是创建一个导出,还是副作用?有了 $derived$effect,你需要在开始时做出更多决策(剧透:90% 的时候你想要 $derived),但未来的你和其他团队成员将更容易处理。

也有一些难以发现的陷阱:

  • $: 仅在渲染前直接更新,这意味着您可以在重新渲染之间读取过时的值
  • $: 每个 tick 只运行一次,这意味着语句可能运行的频率比你想象的要低
  • $: 依赖项通过依赖项的静态分析确定。这在大多数情况下都有效,但在重构过程中可能会以微妙的方式出现问题,例如依赖项被移动到函数中,因此不再可见。
  • $: 语句也通过依赖关系的静态分析进行排序。在某些情况下可能会出现平局,导致排序错误,需要手动干预。在重构代码时,排序也可能中断,一些依赖关系因此不再可见。

最后,它对 TypeScript 不友好(我们的编辑工具必须跳过一些步骤才能使其对 TypeScript 有效),这成为了使 Svelte 的反应模型真正通用的障碍。

$derived$effect 修复这些问题

  • 总是返回最新值
  • 运行以保持稳定所需频率
  • 确定运行时的依赖,因此对重构免疫
  • 执行所需依赖项,因此不受顺序问题影响
  • TypeScript 友好

导出 let → $props

在 Svelte 4 中,组件属性使用 export let 声明。每个属性都是一个单独的声明。在 Svelte 5 中,所有属性都通过 $props 运行符声明,通过解构:

<script>
	export let optional = 'unset';
	export let required;
	let { optional = 'unset', required } = $props();
</script>

存在多种情况,声明属性比几个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();
<script>
	let klass = '';
	export { klass as class};
	let { class: klass, ...rest } = $props();
</script>
<button class={klass} {...$$restPropsrest}>click me</button>

[!详细信息] 我们为什么要这样做 export let 是更具争议性的 API 决定之一,关于你是否应该考虑一个属性是 exportimport,有很多争论。$props 没有这种特性。这也符合其他符文,一般的想法可以归结为“Svelte 中所有与响应性相关的东西都是一个符文”。

也存在许多关于export let的限制,这需要额外的 API,如上所示。$props将这一概念统一为一个语法概念,该概念在很大程度上依赖于常规 JavaScript 解构语法。

活动变更

事件处理器在 Svelte 5 中得到了改进。在 Svelte 4 中,我们使用on:指令将事件监听器附加到元素上,而在 Svelte 5 中,它们像任何其他属性一样(换句话说——去掉冒号):

<script>
	let count = $state(0);
</script>

<button on:click={() => count++}>
	clicks: {count}
</button>

由于它们只是属性,您可以使用正常的缩写语法...

<script>
	let count = $state(0);

	function onclick() {
		count++;
	}
</script>

<button {onclick}>
	clicks: {count}
</button>

...尽管在使用命名事件处理函数时,通常最好使用更具描述性的名称。

组件事件

在 Svelte 4 中,组件可以通过创建一个派发器来发射事件,使用 createEventDispatcher

此函数在 Svelte 5 中已弃用。取而代之,组件应接受回调属性 - 这意味着您将这些函数作为属性传递给这些组件:

App
<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}
Pump
<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>

冒泡事件

而不是使用<button on:click>来将事件从元素转发到组件,组件应接受一个onclick回调属性:

<script>
	let { onclick } = $props();
</script>

<button on:click {onclick}>
	click me
</button>

请注意,这也意味着您可以将事件处理器与其他属性一起“分配”到元素上,而不是逐一 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>

事件修饰符

在 Svelte 4 中,您可以为处理程序添加事件修饰符:

<button on:click|once|preventDefault={handler}>...</button>

修改器仅针对 on: 而言,因此不适用于现代事件处理器。在处理器内部添加如 event.preventDefault() 这样的代码更可取,因为所有逻辑都集中在一个地方,而不是分散在处理器和修改器之间。

由于事件处理器只是函数,您可以根据需要创建自己的包装器:

<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進行處理,我們將修飾符添加到事件名稱中:

<button onclickcapture={...}>...</button>

修改事件处理器的 被动 选项,同时,这不是一件可以轻率行事的事情。如果您有使用它的用例——您可能没有!——那么您将需要使用一个动作来自己应用事件处理器。

多事件处理器

在 Svelte 4 中,这是可能的:

<button on:click={one} on:click={two}>...</button>

元素上不允许重复属性/属性,现在包括事件处理器——不允许这样做:

<button
	onclick={(e) => {
		one(e);
		two(e);
	}}
>
	...
</button>

在传递属性时,本地事件处理器必须放在 传递之后,否则它们的风险是被覆盖:

<button
	{...props}
	onclick={(e) => {
		doStuff(e);
		props.onclick?.(e);
	}}
>
	...
</button>

[!详情] 我们为什么要这样做 createEventDispatcher 总是有点模板化:

  • 导入函数
  • 调用获取调度函数的函数
  • 调用带有字符串和可能的有效负载的调度函数
  • 检索另一端通过一个.detail属性获取该有效载荷,因为该事件始终是一个CustomEvent

它始终可以使用组件回调属性,但因为你必须使用on:监听 DOM 事件,所以在组件事件中出于语法一致性,使用createEventDispatcher是有意义的。现在我们有事件属性(onclick),情况就相反了:回调属性现在更合理。

删除事件修饰符可能是那些喜欢事件修饰符简写语法的人认为的一个退步。鉴于它们并不常用,我们用更明确的表达方式换取了更小的作用域。修饰符也并不一致,因为大多数修饰符仅适用于 DOM 元素。

多个监听器监听同一事件也不再可能,但这本身就是一个反模式,因为它会阻碍可读性:如果有许多属性,除非它们紧挨着,否则很难发现有两个处理程序。这也暗示了这两个处理程序是独立的,但实际上,在one中的 event.stopImmediatePropagation() 会阻止two被调用。

通过弃用createEventDispatcheron:指令,转而使用回调属性和常规元素属性,我们:

  • 降低 Svelte 的学习曲线
  • 删除样板文本,尤其是围绕 createEventDispatcher 的部分
  • 删除创建可能甚至没有监听器的 CustomEvent 对象的开销
  • 增加传播事件处理器的功能
  • 添加能够知道哪些事件处理器被提供给组件的功能
  • 增加表达给定事件处理器是必需还是可选的能力
  • 提高类型安全性(之前,Svelte 实际上无法保证组件不会发出特定事件)

片段而非槽位

在 Svelte 4 中,内容可以通过插槽传递给组件。Svelte 5 用片段取代了它们,这些片段更强大、更灵活,因此插槽在 Svelte 5 中已被弃用。

他们继续工作,然而,你可以将代码片段传递给使用插槽的组件:

Child
<slot />
<hr />
<slot name="foo" message="hello" />
Parent
<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 ...} 标签的组件。)

当使用自定义元素时,您仍然应该像以前一样使用 <slot />。在未来的版本中,当 Svelte 移除其内部版本的插槽时,它将保留这些插槽不变,即输出一个常规的 DOM 标签而不是将其转换。

默认内容

在 Svelte 4 中,将 UI 传递给子组件最简单的方式是使用 <slot />。在 Svelte 5 中,这通过使用 children 属性来完成,然后通过 {@render children()} 显示:

<script>
	let { children } = $props();
</script>

<slot />
{@render children?.()}

多内容占位符

如果您需要多个 UI 占位符,您必须使用命名槽。在 Svelte 5 中,请使用 props,随意命名并使用它们进行渲染...

<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>

回传数据

在 Svelte 4 中,您会将数据传递给 slot,然后在父组件中使用 let: 来检索它。在 Svelte 5 中,代码片段承担了这一责任:

App
<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>
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}

[!详情] 我们为什么要这样做 槽位易于开始使用,但随着用例的日益复杂,语法变得越来越复杂和令人困惑:

  • the let: 语法让很多人感到困惑,因为它 创建 一个变量,而所有其他的 : 指令 接收 一个变量
  • 变量使用let:声明的作用域不明确。在上面的示例中,看起来您可以在empty槽中使用item槽属性,但这并不正确。
  • 命名槽必须使用 slot 属性应用于一个元素。有时你不想创建一个元素,因此我们必须添加 <svelte:fragment> API。
  • 命名槽位也可以应用于组件,这改变了 `let:` 指令可用的语义(即使今天我们维护者也常常不知道它的工作方式)

代码片段通过更易读和清晰的方式解决了所有这些问题。同时,它们更强大,因为它们允许你定义可以渲染到任何地方的 UI 部分,而不仅仅是将它们作为属性传递给组件。

迁移脚本

截至目前,你应该已经对“之前/之后”以及旧语法与新语法的关联有了相当好的理解。可能也变得很明显,许多这些迁移相当技术性和重复——这是你不想手动去做的事情。

我们持相同看法,这就是为什么我们提供了一个迁移脚本来自动完成大部分迁移。您可以通过使用npx sv migrate svelte-5来升级您的项目。这将执行以下操作:

  • 提升您的 package.json 中的核心依赖项
  • 迁移到符文(let$state 等)
  • 迁移到 DOM 元素的属性事件(on:clickonclick
  • 将槽位创建迁移到渲染标签(<slot />{@render children()}
  • 将插槽使用迁移到代码片段(<div slot="x">...</div>{#snippet x()}<div>...</div>{/snippet}
  • 迁移明显的组件创建(new Component(...)mount(Component, ...)

您也可以通过 Migrate Component to Svelte 5 Syntax 命令在 VS Code 中迁移单个组件,或在我们的 Playground 中通过迁移按钮进行迁移。

并非所有内容都可以自动迁移,一些迁移需要在之后进行手动清理。以下部分将更详细地描述这些内容。

运行

您可能会看到迁移脚本将一些您的$:语句转换为从svelte/legacy导入的run函数。这种情况发生在迁移脚本无法可靠地将语句迁移到$derived,并得出这是副作用结论时。在某些情况下,这可能是错误的,最好将其更改为使用$derived。在其他情况下,这可能是正确的,但由于$:语句在服务器上运行,而$effect则不运行,因此将其转换为这样的形式是不安全的。相反,使用run作为临时解决方案。run模仿了大多数$:的特性,即它在服务器上运行一次,并在客户端作为$effect.pre运行($effect.pre在将更改应用到 DOM 之前运行;您可能更希望使用$effect)。

<script>
	import { run } from 'svelte/legacy';
	run(() => {
	$effect(() => {
		// some side effect code
	})
</script>

事件修饰符

事件修饰符不适用于事件属性(例如,您不能这样做 onclick|preventDefault={...})。因此,在将事件指令迁移到事件属性时,我们需要一个函数替换这些修饰符。这些是从 svelte/legacy 导入的,应该迁移开,例如,只需使用 event.preventDefault()

<script>
	import { preventDefault } from 'svelte/legacy';
</script>

<button
	onclick={preventDefault((event) => {
		event.preventDefault();
		// ...
	})}
>
	click me
</button>

事物未自动迁移

迁移脚本不转换 createEventDispatcher。您需要手动调整这些部分。它不这样做是因为风险太高,因为它可能会导致组件用户断开连接,而迁移脚本无法发现这一点。

迁移脚本不转换 beforeUpdate/afterUpdate。它不这样做是因为无法确定代码的实际意图。一般来说,你可以使用 $effect.pre(与beforeUpdate同时运行)和 tick(从svelte导入,允许你等待更改应用到 DOM 上,然后执行一些工作)。

组件不再是类

在 Svelte 3 和 4 中,组件是类。在 Svelte 5 中,它们是函数,并且应该以不同的方式实例化。如果您需要手动实例化组件,应使用 mounthydrate(从 svelte 导入)代替。如果您在使用 SvelteKit 时遇到此错误,请首先尝试更新到最新版本的 SvelteKit,它增加了对 Svelte 5 的支持。如果您在没有使用 SvelteKit 的 Svelte 中,您可能有一个 main.js 文件(或类似文件),您需要调整它:

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<...>): {
    $on?(type: string, callback: (e: any) => void): () => void;
    $set?(props: Partial<Record<string, any>>): void;
} & 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.

mount
(const App: LegacyComponentTypeApp, { target: Document | Element | ShadowRoot

Target element where the component will be mounted.

target
: var document: Documentdocument.Document.getElementById(elementId: string): HTMLElement | null

Returns a reference to the first object with the specified value of the ID attribute.

@paramelementId String that specifies the ID value.
getElementById
("app") });
export default
const app: {
    $on?(type: string, callback: (e: any) => void): () => void;
    $set?(props: Partial<Record<string, any>>): void;
} & Record<string, any>
app
;

挂载激活具有完全相同的 API。区别在于激活会在其目标中提取 Svelte 的服务端渲染 HTML 并激活它。它们都返回一个包含组件导出和可能属性访问器(如果编译时带有accessors: true)的对象。它们不包含您可能从类组件 API 中了解到的$on$set$destroy方法。这些是它们的替代品:

对于$on,而不是监听事件,通过选项参数上的events属性传递它们。

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<...>): {
    $on?(type: string, callback: (e: any) => void): () => void;
    $set?(props: Partial<Record<string, any>>): void;
} & 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.

mount
(const App: LegacyComponentTypeApp, { target: Document | Element | ShadowRoot

Target element where the component will be mounted.

target
: var document: Documentdocument.Document.getElementById(elementId: string): HTMLElement | null

Returns a reference to the first object with the specified value of the ID attribute.

@paramelementId String that specifies the ID value.
getElementById
("app"), events?: Record<string, (e: any) => any> | undefined

Allows the specification of events.

@deprecatedUse callback props instead.
events
: { event: anyevent: callback } });

[!注意] 注意使用 事件 是不建议的——相反,使用回调

对于$set,请使用$state来创建一个响应式属性对象并进行操作。如果您在.js.ts文件中执行此操作,请调整扩展名以包含.svelte,即.svelte.js.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

Declares reactive state.

Example:

let count = $state(0);

https://svelte.dev/docs/svelte/$state

@paraminitial The initial value
$state
({ foo: stringfoo: '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<...>): {
    $on?(type: string, callback: (e: any) => void): () => void;
    $set?(props: Partial<Record<string, any>>): void;
} & 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.

mount
(const App: LegacyComponentTypeApp, { target: Document | Element | ShadowRoot

Target element where the component will be mounted.

target
: var document: Documentdocument.Document.getElementById(elementId: string): HTMLElement | null

Returns a reference to the first object with the specified value of the ID attribute.

@paramelementId String that specifies the ID value.
getElementById
("app"), props?: Record<string, any> | undefined

Component properties.

props
});
const props: {
    foo: string;
}
props
.foo: stringfoo = 'baz';

对于$destroy,请使用unmount代替。

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 });
unmount
} 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.$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<...>): {
    $on?(type: string, callback: (e: any) => void): () => void;
    $set?(props: Partial<Record<string, any>>): void;
} & 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.

mount
(const App: LegacyComponentTypeApp, { target: Document | Element | ShadowRoot

Target element where the component will be mounted.

target
: var document: Documentdocument.Document.getElementById(elementId: string): HTMLElement | null

Returns a reference to the first object with the specified value of the ID attribute.

@paramelementId String that specifies the ID value.
getElementById
("app") });
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 });
unmount
(
const app: {
    $on?(type: string, callback: (e: any) => void): () => void;
    $set?(props: Partial<Record<string, any>>): void;
} & Record<string, any>
app
);

作为临时解决方案,您也可以使用createClassComponentasClassComponent(从svelte/legacy导入)来保持与 Svelte 4 相同的 API,在实例化后使用。

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.

@deprecatedUse this only as a temporary solution to migrate your imperative component code to Svelte 5.
createClassComponent
} from 'svelte/legacy';
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: 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.

@deprecatedUse this only as a temporary solution to migrate your imperative component code to Svelte 5.
createClassComponent
({ component: Component<Record<string, any>, {}, string> | ComponentType<SvelteComponent<Record<string, any>, any, any>>component: const App: LegacyComponentTypeApp, ComponentConstructorOptions<Props extends Record<string, any> = Record<string, any>>.target: Document | Element | ShadowRoottarget: var document: Documentdocument.Document.getElementById(elementId: string): HTMLElement | null

Returns a reference to the first object with the specified value of the ID attribute.

@paramelementId String that specifies the ID value.
getElementById
("app") });
export default const app: SvelteComponent<Record<string, any>, any, any> & Record<string, any>app;

如果此组件不在您的控制之下,您可以使用compatibility.componentApi编译器选项来自动应用向后兼容性,这意味着使用new Component(...)编写的代码无需调整即可继续工作(请注意,这会给每个组件添加一点开销)。这还将为通过bind:this获取的所有组件实例添加$set$on方法。

/// svelte.config.js
export default {
	
compilerOptions: {
    compatibility: {
        componentApi: number;
    };
}
compilerOptions
: {
compatibility: {
    componentApi: number;
}
compatibility
: {
componentApi: numbercomponentApi: 4 } } };

请注意,mounthydrate 并非同步,因此像 onMount 这样的函数在函数返回时可能尚未被调用,挂起的 Promise 块也尚未渲染(因为 #await 等待一个可能立即解决的 Promise 的微任务)。如果您需要这个保证,请在调用 mount/hydrate 之后调用 flushSync(从 'svelte' 导入)。

服务器 API 更改

类似地,当组件编译为服务器端渲染时,不再具有 render 方法。相反,从 svelte/server 将函数传递给 render

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>;
    idPrefix?: string;
}] : [component: Comp extends SvelteComponent<any> ? ComponentType<Comp> : Comp, options: {
    props: Omit<Props, "$$slots" | "$$events">;
    context?: Map<any, any>;
    idPrefix?: string;
}]): 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.

render
} from 'svelte/server';
import
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: stringhtml, const head: string

HTML that goes into the &#x3C;head>

head
} =
render<SvelteComponent<Record<string, any>, any, any>, Record<string, any>>(component: ComponentType<SvelteComponent<Record<string, any>, any, any>>, options?: {
    ...;
} | undefined): 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.

render
(const App: LegacyComponentTypeApp, { props?: Omit<Record<string, any>, "$$slots" | "$$events"> | undefinedprops: { message: stringmessage: 'hello' }});

在 Svelte 4 中,将组件渲染为字符串时也返回了所有组件的 CSS。在 Svelte 5 中,默认情况下不再是这样,因为大多数情况下你使用的是其他方式(如 SvelteKit)来处理它。如果你需要从render返回 CSS,可以将css编译器选项设置为'injected',它将为head添加<style>元素。

组件类型更改

类向函数的转变也体现在类型定义中:`SvelteComponent`,Svelte 4 的基类,已被弃用,转而使用新的 `Component` 类型,该类型定义了 Svelte 组件的函数形状。要在 `d.ts` 文件中手动定义组件形状:

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&#x3C;{ 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:

&#x3C;script lang="ts">
	import { MyComponent } from "component-library";
&#x3C;/script>
&#x3C;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&#x3C;{ 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:

&#x3C;script lang="ts">
	import { MyComponent } from "component-library";
&#x3C;/script>
&#x3C;MyComponent foo={'bar'} />
Component
<{
foo: stringfoo: string; }>;

声明某个类型组件是必需的:

import { import ComponentAComponentA, import ComponentBComponentB } 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&#x3C;{ 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:

&#x3C;script lang="ts">
	import { MyComponent } from "component-library";
&#x3C;/script>
&#x3C;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 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&#x3C;{ 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:

&#x3C;script lang="ts">
	import { MyComponent } from "component-library";
&#x3C;/script>
&#x3C;MyComponent foo={'bar'} />
Component
<{ foo: stringfoo: string }> =
function $state<any>(initial: any): any (+1 overload)
namespace $state

Declares reactive state.

Example:

let count = $state(0);

https://svelte.dev/docs/svelte/$state

@paraminitial The initial value
$state
(
var Math: Math

An intrinsic object that provides basic mathematics functionality and constants.

Math
.Math.random(): number

Returns a pseudorandom number between 0 and 1.

random
() ? import ComponentAComponentA : import ComponentBComponentB
);

两个实用类型ComponentEventsComponentType也已弃用。ComponentEvents已过时,因为事件现在定义为回调属性,而ComponentType已过时,因为新的Component类型已经是组件类型了(即 ComponentType<SvelteComponent<{ prop: string }>> 等同于Component<{ prop: string }>)。

bind:这会改变

因为组件不再是类,使用bind:this不再返回带有$set$on$destroy方法的类实例。它只返回实例导出(export function/const)和,如果你使用了accessors选项,每个属性的 getter/setter 对。

<svelte:component> 不再必要

在 Svelte 4 中,组件是静态的静态——如果你渲染<Thing>,并且Thing的值发生变化,则不会发生任何事。要使其动态,你必须使用<svelte:component>

这不再适用于 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),以区分元素,除非使用点符号。

点表示法表示一个组件

在 Svelte 4 中,<foo.bar>会创建一个标签名为"foo.bar"的元素。在 Svelte 5 中,foo.bar被视为一个组件。这在each块内部尤其有用。

{#each items as item}
	<item.component {...item.props} />
{/each}

空格处理已更改

之前,Svelte 使用一个非常复杂的算法来决定是否保留空白字符。Svelte 5 简化了这一点,这使得开发者更容易理解。规则是:

  • 节点之间的空白被折叠为一个空白
  • 空白字符在标签开头和结尾将被完全删除
  • 某些例外适用,例如保留 pre 标签内的空白字符

如前所述,您可以通过在编译器设置中或每个组件的基础上设置 preserveWhitespace 选项来禁用空白字符修剪。或者,在 <svelte:options> 中进行设置。

现代浏览器必需

Svelte 5 需要现代浏览器(换句话说,不是 Internet Explorer)出于各种原因:

  • 它使用 代理
  • 元素具有 clientWidth / clientHeight/offsetWidth/offsetHeight 绑定使用 ResizeObserver 而不是复杂的 <iframe> 诡计
  • <input type="range" bind:value={...} /> 仅使用一个 输入 事件监听器,而不是也监听 更改 事件作为后备

The legacy 编译器选项,该选项生成的代码体积更大但兼容 IE,现已不存在。

编译器选项更改

  • The false / true(之前已弃用)和"none"值已从css选项中移除作为有效值
  • The legacy option was repurposed
  • The hydratable 选项已被移除。Svelte 组件现在始终是可水合的。
  • The enableSourcemap 选项已被移除。现在始终生成源映射,工具可以选择忽略它
  • The tag选项已被移除。请使用组件内的 <svelte:options customElement="tag-name" /> 代替。
  • The loopGuardTimeoutformatsveltePatherrorModevarsReport 选项已被移除

The children prop is reserved

组件标签内的内容成为名为children的片段属性。您不能有同名的单独属性。

runes 模式中的破坏性更改

一些重大更改仅在您的组件处于符文模式时才适用。

组件导出的绑定不允许

导出自 runes 模式组件的内容不能直接绑定。例如,在组件 A 中有 export const foo = ...,然后执行 <A bind:foo /> 会导致错误。请使用 bind:this 代替 —— <A bind:this={a} /> —— 并通过 a.foo 访问导出内容。此更改使得推理更加容易,因为它强制了 props 和导出之间的清晰分离。

绑定需要使用$bindable()显式定义

在 Svelte 4 语法中,每个属性(通过export let声明)都是可绑定的,这意味着你可以bind:到它。在 runes 模式下,属性默认不可绑定:你需要用$bindable符标示可绑定属性。

如果可绑定属性具有默认值(例如 let { foo = $bindable('bar') } = $props(); ),如果您正在将其绑定,则需要向该属性传递一个非 undefined 的值。这可以防止模糊行为——父元素和子元素必须具有相同的值——并提高性能(在 Svelte 4 中,默认值会反映回父元素,导致浪费额外的渲染周期)。

访问器选项被忽略

设置 访问器 选项为 true 使组件的属性直接在组件实例上可访问。

<svelte:options accessors={true} />

<script>
	// available via componentInstance.name
	export let name;
</script>

在符文模式下,属性在组件实例上不可访问。如果需要公开它们,可以使用组件导出。

<script>
	let { name } = $props();
	// available via componentInstance.getName()
	export const getName = () => name;
</script>

或者,如果它们被实例化的位置在你控制之下,你也可以通过调整它们的结尾以包含 .svelte,即 .svelte.js.svelte.ts,在 .js/.ts 文件中使用这些符文,然后使用 $state

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.foo = 'baz' const
const props: {
    foo: string;
}
props
=
function $state<{
    foo: string;
}>(initial: {
    foo: string;
}): {
    foo: string;
} (+1 overload)
namespace $state

Declares reactive state.

Example:

let count = $state(0);

https://svelte.dev/docs/svelte/$state

@paraminitial The initial value
$state
({ foo: stringfoo: '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<...>): {
    $on?(type: string, callback: (e: any) => void): () => void;
    $set?(props: Partial<Record<string, any>>): void;
} & 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.

mount
(const App: LegacyComponentTypeApp, { target: Document | Element | ShadowRoot

Target element where the component will be mounted.

target
: var document: Documentdocument.Document.getElementById(elementId: string): HTMLElement | null

Returns a reference to the first object with the specified value of the ID attribute.

@paramelementId String that specifies the ID value.
getElementById
("app"), props?: Record<string, any> | undefined

Component properties.

props
});
const props: {
    foo: string;
}
props
.foo: stringfoo = 'baz';

不可变选项被忽略

设置 不可变 选项在 runes 模式下没有效果。这个概念被 $state 及其变体的工作方式所取代。

类不再“自动响应”

在 Svelte 4 中,执行以下操作会触发响应性:

<script>
	let foo = new Foo();
</script>

<button on:click={() => (foo.value = 1)}>{foo.value}</button
>

这是因为在 Svelte 编译器中,将赋值给foo.value视为更新任何引用foo的指令。在 Svelte 5 中,响应性是在运行时而不是编译时确定的,因此你应该在Foo类上定义$state为响应性的value字段。将new Foo()包裹在$state(...)中不会有任何效果——只有纯对象和数组会被深度响应化。

触摸和滚轮事件是被动

当使用onwheelonmousewheelontouchstartontouchmove事件属性时,处理程序是passive以与浏览器默认设置保持一致。这大大提高了响应速度,允许浏览器立即滚动文档,而不是等待事件处理程序调用event.preventDefault()

在极少数需要防止这些事件默认行为的情况下,您应该使用 on 代替(例如在操作中)。

属性/prop 语法更严格

在 Svelte 4 中,复杂的属性值无需加引号:

<Component prop=this{is}valid />

这是一个 footgun。在 runes 模式下,如果你想连接东西,你必须用引号括住值:

<Component prop="this{is}valid" />

请注意,Svelte 5 也会警告您如果有一个单行表达式被引号包围,例如 answer="{42}" —— 在 Svelte 6 中,这将导致值被转换为字符串,而不是作为数字传递。

HTML 结构更严格

在 Svelte 4 中,您被允许编写浏览器在服务器端渲染时进行修复的 HTML 代码。例如,您可以编写这样的...

<table>
	<tr>
		<td>hi</td>
	</tr>
</table>

...并且浏览器会自动插入一个 <tbody> 元素:

<table>
	<tbody>
		<tr>
			<td>hi</td>
		</tr>
	</tbody>
</table>

Svelte 5 对 HTML 结构要求更严格,在浏览器会修复 DOM 的情况下,编译器将抛出错误。

其他重大变更

更严格的 @const 赋值验证

任务不再允许分配给@const声明的解构部分。这曾经被允许是一个疏忽。

:is(...), :has(...), 和 :where(...) 是作用域

之前,Svelte 没有分析 :is(...):has(...):where(...) 内部的选择器,实际上将它们视为全局的。Svelte 5 在当前组件的上下文中分析它们。因此,一些选择器现在可能被视为未使用,如果它们依赖于这种处理。要修复此问题,请在 :is(...)/:has(...)/:where(...) 选择器内部使用 :global(...)

当使用 Tailwind 的 @apply 指令时,添加一个 :global 选择器以保留使用 Tailwind 生成的 :is(...) 选择器的规则:

main :global {
	@apply bg-blue-100 dark:bg-blue-900;
}

CSS 散列位置不再确定

之前 Svelte 总是将 CSS 哈希值插入到最后。在 Svelte 5 中,这不再保证。只有在您有非常奇怪的 CSS 选择器时,这才会造成破坏。

作用域 CSS 使用:where(...)

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,但代价是可能出现不可预测的特定性变化:

css = css.replace(/:where\((.+?)\)/, '$1');

错误/警告代码已被重命名

错误和警告代码已重命名。之前它们使用破折号分隔单词,现在使用下划线(例如,foo-bar 变为 foo_bar)。此外,一些代码的措辞略有改动。

减少命名空间数量

您可以向编译器选项 namespace 传递的有效命名空间数量已减少到 html(默认值),mathmlsvg

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 回调现在将在任何子组件的 afterUpdate 回调之后运行。

beforeUpdate/afterUpdate 当组件包含一个 <slot> 且其内容更新时不再运行。

两个函数在 runes 模式下均不允许使用 — 请使用 $effect.pre(...)$effect(...) 代替。

可编辑内容行为变更

如果您有一个具有相应绑定内部反应值contenteditable节点(例如: <div contenteditable=true bind:textContent>count is {count}</div> ),那么由于绑定立即完全控制内容,内容 editable 中的值不会因count的更新而更新,它应该只通过它来更新。

oneventname 属性不再接受字符串值

在 Svelte 4 中,可以将事件属性作为字符串指定在 HTML 元素上:

<button onclick="alert('hello')">...</button>

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. 

空值未定义 变为空字符串

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 values can only be null, undefined or 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

绑定现在响应表单重置

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 不再导出

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被禁止

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> elements in declarative shadow roots are preserved

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> tag must be an expression

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"}>

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 plays transitions by default

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={...}> and {@html ...} hydration mismatches are not repaired

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. 

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 works differently

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 attributes are delegated

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 uses a different element

Svelte 5 uses an extra <svelte-css-wrapper> element instead of a <div> to wrap the component when using CSS custom properties.

Edit this page on GitHub llms.txt