首页 / Svelte 5 入门教程 / Svelte 4 迁移到 Svelte 5

Svelte 5 入门教程

Svelte 4 迁移到 Svelte 5

本教程共 50 篇 · 第 42 篇 · 更新于 2026-08-05 · 约 5 分钟阅读

SvelteSvelte 5迁移Svelte 4升级

本节目标:学会用迁移脚本和手动方式把 Svelte 4 代码迁移到 Svelte 5 Runes 语法,掌握所有核心 API 的旧新对照。

渐进式迁移

Svelte 5 仍然支持 Svelte 4 的旧语法。你不需要一次性全部迁移,可以新旧混用,逐个组件改造。这让迁移风险大大降低。

迁移脚本

官方提供了自动迁移脚本:

npx sv migrate svelte-5

它会自动完成以下工作:

  • 升级 package.json 中的核心依赖版本
  • let$state
  • on:clickonclick
  • <slot />{@render children?.()}
  • <div slot="x">{#snippet x()}<div>{/snippet}
  • new Component(...)mount(Component, ...)
Tip

在 VS Code 中可以右键单个组件,选择 “Migrate Component to Svelte 5 Syntax” 来逐个迁移。在官方 Playground 上也有 “Migrate” 按钮。

脚本不能完成所有迁移,有些需要手动处理。下面是核心对照表。

旧写法对照表

用途Svelte 4Svelte 5
组件状态let count = 0let count = $state(0)
计算值$: double = count * 2let double = $derived(count * 2)
副作用$: { ... }$effect(() => { ... })
Propsexport let foolet { foo } = $props()
双向绑定export let value; bind:valuelet { value = $bindable() } = $props()
事件on:click={fn}onclick={fn}
组件事件createEventDispatcher回调 props
默认插槽<slot />{@render children?.()}
具名插槽<slot name="header" />{@render header()}
创建组件new App({ target })mount(App, { target })
销毁app.$destroy()unmount(app)
更新 propsapp.$set({ x: 1 })props.x = 1(props 用 $state
所有 props$$propslet props = $props()
剩余 props$$restPropslet { foo, ...rest } = $props()

let → $state

Svelte 4 中 let 声明在组件顶层自动是响应式的。Svelte 5 需要显式用 $state

<!-- Svelte 4 -->
<script>
	let count = 0;
</script>

<!-- Svelte 5 -->
<script>
	let count = $state(0);
</script>

Svelte 4 中数组用 .push() 不会触发更新,需要重新赋值。Svelte 5 中 $state 是深度响应式的,直接 .push() 就行:

<!-- Svelte 4 -->
<script>
	let items = [];
	function add() {
		items.push('new');
		items = items; // 需要重新赋值触发更新
	}
</script>

<!-- Svelte 5 -->
<script>
	let items = $state([]);
	function add() {
		items.push('new'); // 直接生效,不需要重新赋值
	}
</script>

$: → $derived / $effect

Svelte 4 的 $: 既用于计算值也用于副作用。Svelte 5 拆成了两个:

<!-- Svelte 4 -->
<script>
	let count = 0;
	$: double = count * 2;        // 计算值
	$: {                          // 副作用
		if (count > 5) alert('太多!');
	}
</script>

<!-- Svelte 5 -->
<script>
	let count = $state(0);
	let double = $derived(count * 2);   // 计算值
	$effect(() => {                      // 副作用
		if (count > 5) alert('太多!');
	});
</script>
Note

90% 的情况应该用 $derived$effect 是最后手段。

情况用什么
纯计算 → 新值$derived
有副作用(DOM 操作、日志、外部同步)$effect

export let → $props

Svelte 4 的 export let 改为 $props() 解构:

<!-- Svelte 4 -->
<script>
	export let optional = '默认值';
	export let required;
	export { klass as class };
</script>

<!-- Svelte 5 -->
<script>
	let { optional = '默认值', required, class: klass } = $props();
</script>

$$props$$restProps 用标准解构语法替代:

<!-- Svelte 4 -->
<button class={klass} {...$$restProps}>...</button>

<!-- Svelte 5 -->
<script>
	let { class: klass, ...rest } = $props();
</script>

<button class={klass} {...rest}>...</button>

on:click → onclick

事件指令改成属性形式,去掉冒号:

<!-- Svelte 4 -->
<button on:click={handleClick}>点击</button>

<!-- Svelte 5 -->
<button onclick={handleClick}>点击</button>

属性简写也支持:

<button {onclick}>点击</button>

事件修饰符

Svelte 4 的 on:click|preventDefault|once 在 Svelte 5 中不支持。改为在处理器里手动处理:

<!-- Svelte 4 -->
<button on:click|preventDefault={handler}>提交</button>

<!-- Svelte 5 -->
<script>
	function handler(e) {
		e.preventDefault();
		// 你的逻辑
	}
</script>

<button onclick={handler}>提交</button>

capture 修饰符改为属性名后缀:

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

createEventDispatcher → 回调 props

这是改动最大的部分。Svelte 4 用 createEventDispatcher 发送事件,Svelte 5 改用回调 props:

<!-- Pump.svelte (Svelte 4) -->
<script>
	import { createEventDispatcher } from 'svelte';
	const dispatch = createEventDispatcher();
</script>

<button on:click={() => dispatch('inflate', 5)}>充气</button>
<!-- Pump.svelte (Svelte 5) -->
<script>
	let { inflate } = $props();
</script>

<button onclick={() => inflate(5)}>充气</button>

父组件那边:

<!-- Svelte 4 -->
<Pump on:inflate={(e) => size += e.detail} />

<!-- Svelte 5 -->
<Pump inflate={(power) => size += power} />
Tip

回调 props 比事件分发更直接。不需要 import、不需要 dispatch、不需要 .detail,就是普通函数调用。

slot → snippet

<slot /> 改为 children prop 加 {@render}

<!-- Svelte 4 -->
<slot />

<!-- Svelte 5 -->
<script>
	let { children } = $props();
</script>

{@render children?.()}

具名插槽改为命名的 snippet prop:

<!-- Svelte 4 -->
<slot name="header" />
<slot name="footer" />

<!-- Svelte 5 -->
<script>
	let { header, footer } = $props();
</script>

{@render header?.()}
{@render footer?.()}

迁移脚本的手动清理

脚本会把无法自动判断的 $: 语句转成 svelte/legacyrun 函数。你需要手动改成 $effect$derived

<!-- 脚本输出 -->
<script>
	import { run } from 'svelte/legacy';
	run(() => {
		// 副作用代码
	});
</script>

<!-- 手动改为 -->
<script>
	$effect(() => {
		// 副作用代码
	});
</script>

事件修饰符会被转成 svelte/legacy 的包装函数,同样需要手动清理:

<!-- 脚本输出 -->
<script>
	import { preventDefault } from 'svelte/legacy';
</script>
<button onclick={preventDefault(handler)}>...</button>

<!-- 手动改为 -->
<script>
	function handler(e) {
		e.preventDefault();
		// 你的逻辑
	}
</script>
<button onclick={handler}>...</button>

本节回顾

  • npx sv migrate svelte-5 自动迁移大部分代码,但需要手动清理
  • let$state,数组可直接 .push() 触发更新
  • $: 计算值 → $derived,副作用 → $effect
  • export let$props() 解构,$$restProps → rest 解构
  • on:clickonclick,事件修饰符改为手动处理
  • createEventDispatcher → 回调 props,更简单直接
  • <slot>children prop + {@render children()}
  • 迁移脚本输出的 svelte/legacy 函数需手动改为 $effect 或内联处理