Skip to content

从 Svelte 4 迁移

Svelte 5 几乎完全向后兼容 Svelte 4。你可以渐进式迁移,旧的写法(legacy mode)和新的 Runes 可以在同一项目里共存。本章给出主要变化与迁移步骤。

迁移策略

推荐渐进式而非一次性重写:

  1. 先升级依赖到 Svelte 5,确认项目能跑(此时用的是兼容/legacy 模式)
  2. 逐个组件改用 Runes
  3. 用官方 codemod 自动转换大部分语法

升级依赖

bash
npm install svelte@latest

# SvelteKit 项目同时升级
npm install @sveltejs/kit@latest @sveltejs/vite-plugin-svelte@latest

确认版本:

bash
npm ls svelte   # 应显示 5.x

使用官方迁移工具(codemod)

Svelte 团队提供了自动迁移工具,能把大部分 Svelte 4 语法转成 Runes:

bash
npx sv migrate

它会交互式地处理 export let$props$:$derived/$effecton: → 事件属性等转换。转换后仍需人工复核。

主要语法变化对照

1. 响应式状态

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

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

2. 派生值

svelte
<!-- Svelte 4 -->
<script>
  let count = 0;
  $: doubled = count * 2;
</script>

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

3. 副作用

svelte
<!-- Svelte 4 -->
<script>
  let count = 0;
  $: console.log(count);
</script>

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

4. Props

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

<!-- Svelte 5 -->
<script>
  let { title, count = 0 } = $props();
</script>

5. 事件

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

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

6. 组件自定义事件

svelte
<!-- Svelte 4 -->
<script>
  import { createEventDispatcher } from 'svelte';
  const dispatch = createEventDispatcher();
</script>
<button on:click={() => dispatch('notify', data)}>通知</button>

<!-- Svelte 5 -->
<script>
  let { onnotify } = $props();
</script>
<button onclick={() => onnotify(data)}>通知</button>

7. Slots → Snippets

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

<!-- Svelte 5 -->
<script>let { children, header } = $props();</script>
{@render children()}
{@render header?.()}

8. 双向绑定的 props

svelte
<!-- Svelte 4 -->
<script>
  export let value;  // 父组件 bind:value 即可改
</script>

<!-- Svelte 5 -->
<script>
  let { value = $bindable() } = $props();  // 显式声明可绑定
</script>

9. 挂载 API

js
// Svelte 4
const app = new App({ target: document.body });

// Svelte 5
import { mount } from 'svelte';
const app = mount(App, { target: document.body });

完整对照表

主题Svelte 4Svelte 5
状态let x = 0let x = $state(0)
派生$: y = x * 2let y = $derived(x * 2)
副作用$: sideEffect()$effect(() => sideEffect())
Propsexport let plet { p } = $props()
可绑定 propexport let v(配 bind:let { v = $bindable() } = $props()
DOM 事件on:clickonclick
组件事件createEventDispatcher回调 props(onxxx
内容分发<slot>{@render children()}
具名内容<slot name="x">Snippets {#snippet x()}
挂载new App({ target })mount(App, { target })
深度响应式需重新赋值Proxy 自动深度追踪

深度响应式的行为变化

这是迁移时最容易踩坑的地方。Svelte 4 需要「重新赋值」来触发更新:

svelte
<!-- Svelte 4:必须重新赋值 -->
<script>
  let user = { name: '张三' };
  function rename() {
    user.name = '李四';
    user = user; // ← 手动触发更新(或 user = {...user})
  }
</script>

Svelte 5 的 $state 是深度响应式的,不需要这行:

svelte
<!-- Svelte 5 -->
<script>
  let user = $state({ name: '张三' });
  function rename() {
    user.name = '李四'; // 直接生效
  }
</script>

迁移检查清单

  • [ ] 升级 svelte 到 5.x,项目能正常构建运行
  • [ ] 运行 npx sv migrate 自动转换
  • [ ] 把 export let 改为 $props() 解构
  • [ ] 把 $: 改为 $derived / $effect
  • [ ] 把 on:事件 改为 on事件 属性
  • [ ] 把 createEventDispatcher 改为回调 props
  • [ ] 把 <slot> 改为 Snippets + {@render}
  • [ ] 把需要双向绑定的 prop 加上 $bindable
  • [ ] 删除多余的 x = x 手动触发更新的代码
  • [ ] 用 npx svelte-check 检查类型与编译错误

参考

下一步

有疑问?看常见问题

基于 VitePress 构建 · 示例使用 Svelte 5.57