Svelte 5 深度解析:Runes 革命与响应式编程的范式跃迁
一、引言:Svelte 的崛起
Svelte 是由 Rich Harris 于 2016 年创建的创新性前端框架。与传统框架(如 React、Vue)不同,Svelte 在构建时(build time)编译框架代码,而不是在运行时(runtime)执行虚拟 DOM diffing。这种独特的编译时方法让 Svelte 应用具有出色的性能。
截至 2026 年初,Svelte 的 npm 周下载量突破 800 万次,GitHub Star 数超过 75K+,成为前端框架领域增长最快的框架之一。Svelte 已被用于构建众多知名应用,包括 Netflix、Spotify、Apple、Microsoft 等公司的部分产品。
2025 年 11 月,Svelte 团队正式发布了 Svelte 5,这是一个具有里程碑意义的版本,引入了全新的 Runes 系统,从根本上改变了 Svelte 的响应式编程模式。
Svelte 5 的主要目标是:
- 统一响应式系统:用 Runes 替代之前的响应式声明
- 更好的 TypeScript 支持:原生 TypeScript 支持,无需额外配置
- 更小的包体积:进一步减小运行时大小
- 更好的性能:优化编译输出,提升运行效率
本文将深入解析 Svelte 5 的核心新特性,结合实际代码示例,帮助你快速上手并平滑迁移。
二、Svelte 5 的核心变革:Runes 系统
2.1 什么是 Runes
Runes(符文)是 Svelte 5 引入的新语法,用于声明响应式状态和计算值。它们看起来像函数调用,但实际上是由 Svelte 编译器处理的特殊语法。
Svelte 5 Runes 概览
┌─────────────────────────────────────────────────────────────────┐
│ Runes 系统 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ $state() → 创建响应式状态 │
│ $derived() → 创建派生状态(计算值) │
│ $effect() → 创建副作用(类似 useEffect) │
│ $props() → 定义组件 props │
│ $bindable() → 定义可绑定的 props │
│ $render() → 定义渲染逻辑 │
│ │
└─────────────────────────────────────────────────────────────────┘
2.2 $state:创建响应式状态
在 Svelte 4 中,我们使用 let 声明变量来创建响应式状态:
<!-- Svelte 4 -->
<script>
let count = 0; // 自动响应式
function increment() {
count++;
}
</script>
<button on:click={increment}>
Count: {count}
</button>
在 Svelte 5 中,我们使用 $state 来明确声明响应式状态:
<!-- Svelte 5 -->
<script>
let count = $state(0);
function increment() {
count++;
}
</script>
<button onclick={increment}>
Count: {count}
</button>
2.3 $state 的高级用法
2.3.1 深度响应式对象
<script>
// 普通值
let count = $state(0);
// 对象(自动深度响应式)
let user = $state({
name: 'Alice',
age: 30,
address: {
city: 'Beijing',
street: '123 Main St'
}
});
// 数组(自动响应式)
let items = $state([
{ id: 1, title: 'Item 1' },
{ id: 2, title: 'Item 2' }
]);
function updateName() {
// 直接修改嵌套属性
user.name = 'Bob';
// 同样响应式
user.address.city = 'Shanghai';
}
function addItem() {
items.push({ id: Date.now(), title: 'New Item' });
}
</script>
<div>
<p>{user.name}, {user.age}</p>
<p>{user.address.city}</p>
<button onclick={updateName}>Update Name</button>
<ul>
{#each items as item}
<li>{item.title}</li>
{/each}
</ul>
<button onclick={addItem}>Add Item</button>
</div>
2.3.2 类实例
<script>
class Counter {
count = $state(0);
increment() {
this.count++;
}
get doubled() {
return this.count * 2;
}
}
const counter = new Counter();
</script>
<p>Count: {counter.count}</p>
<p>Doubled: {counter.doubled}</p>
<button onclick={() => counter.increment()}>Increment</button>
2.4 $derived:创建派生状态
$derived 用于创建基于其他状态的计算值,类似于 Vue 中的 computed 或 React 中的 useMemo。
<script>
let count = $state(10);
let multiplier = $state(2);
// 简单的派生计算
let doubled = $derived(count * 2);
// 复杂的派生计算
let result = $derived({
product: count * multiplier,
sum: count + multiplier,
difference: count - multiplier
});
// 派生值作为数组
let items = $state([1, 2, 3, 4, 5]);
let filteredItems = $derived(items.filter(n => n > 2));
let doubledItems = $derived(filteredItems.map(n => n * 2));
</script>
<p>Doubled: {doubled}</p>
<p>Result: {result.product}, {result.sum}, {result.difference}</p>
<p>Filtered: {filteredItems.join(', ')}</p>
<p>Doubled: {doubledItems.join(', ')}</p>
2.5 $effect:处理副作用
$effect 用于处理副作用,类似于 React 的 useEffect 和 Vue 的 watchEffect。
<script>
let count = $state(0);
let userId = $state(1);
// 基础用法
$effect(() => {
console.log('Count changed:', count);
});
// 带依赖的 effect
$effect(() => {
// 当 userId 变化时执行
fetchUser(userId);
});
// 清理函数
$effect(() => {
const interval = setInterval(() => {
count++;
}, 1000);
// 返回清理函数
return () => {
clearInterval(interval);
};
});
</script>
<p>Count: {count}</p>
<p>User ID: {userId}</p>
<button onclick={() => userId++}>Change User</button>
2.6 Snippets:新的模板语法
Svelte 5 引入了 Snippets(代码片段),一种新的可复用模板单元。
<script>
// 定义一个 snippet
let renderCard = $snippet();
const Card = (props) => {
return (
<div class="card">
<h3>{props.title}</h3>
<p>{props.content}</p>
</div>
);
};
// 另一种方式:使用 {#snippet} 语法
function CardSnippet({ title, content }) {
return (
<div class="card">
<h3>{title}</h3>
<p>{content}</p>
</div>
);
}
</script>
<!-- 使用 snippet -->
{@render CardSnippet({ title: 'Hello', content: 'World' })}
2.7 $props:定义组件属性
<!-- Child.svelte -->
<script>
// 定义 props
let { name, age = 18, onIncrement } = $props();
</script>
<div>
<p>Name: {name}</p>
<p>Age: {age}</p>
<button onclick={onIncrement}>Increment Age</button>
</div>
<!-- Parent.svelte -->
<script>
import Child from './Child.svelte';
let age = $state(25);
function handleIncrement() {
age++;
}
</script>
<Child name="Alice" {age} onIncrement={handleIncrement} />
2.8 $bindable:可绑定的 Props
<!-- InputField.svelte -->
<script>
// 可绑定的 prop
let { value = $bindable('') } = $props();
</script>
<input bind:value />
<!-- Parent.svelte -->
<script>
import InputField from './InputField.svelte';
let inputValue = $state('');
</script>
<InputField bind:value={inputValue} />
<p>Input: {inputValue}</p>
三、从 Svelte 4 迁移到 Svelte 5
3.1 破坏性变更
Svelte 5 引入了一些破坏性变更:
主要破坏性变更
1. 响应式声明方式改变
- Svelte 4: let count = 0
- Svelte 5: let count = $state(0)
2. 生命周期钩子改变
- Svelte 4: onMount, onDestroy
- Svelte 5: $effect (带清理)
3. 事件处理改变
- Svelte 4: on:click
- Svelte 5: onclick
4. 组件 API 改变
- Svelte 4: export let prop
- Svelte 5: let { prop } = $props()
5. store 改变
- Svelte 4: writable, readable, derived
- Svelte 5: $state 在模块级别
3.2 迁移策略
3.2.1 自动迁移工具
# 安装 Svelte 官方迁移工具
npx svelte-migrate@latest svelte-5 my-project
# 查看迁移报告
cat svelte-migrate-report.md
3.2.2 手动迁移示例
<!-- Svelte 4 -->
<script>
import { onMount } from 'svelte';
export let title;
export let items = [];
let count = 0;
let doubled = count * 2;
onMount(() => {
console.log('Component mounted');
});
function handleClick() {
count++;
}
</script>
<button on:click={handleClick}>
{title}: {count} (doubled: {doubled})
</button>
<!-- Svelte 5 -->
<script>
// Props 定义
let { title, items = [] } = $props();
// 响应式状态
let count = $state(0);
// 派生状态
let doubled = $derived(count * 2);
// 副作用
$effect(() => {
console.log('Component mounted');
});
function handleClick() {
count++;
}
</script>
<button onclick={handleClick}>
{title}: {count} (doubled: {doubled})
</button>
四、Svelte 5 性能优化
4.1 编译时优化
Svelte 5 的编译器进一步优化了生成的代码:
| 优化项 | Svelte 4 | Svelte 5 | 提升 |
|---|---|---|---|
| 包体积 | 12KB | 8KB | 33% |
| 运行时性能 | 基准 | 15% 提升 | 15% |
| 初始渲染时间 | 基准 | 20% 提升 | 20% |
| 内存占用 | 基准 | 10% 降低 | 10% |
4.2 运行时优化
<script>
// 使用 $state.snapshot 进行不可变更新
let items = $state([{ id: 1, name: 'Alice' }]);
// 更新时创建新引用(不可变模式)
function updateItem() {
items = $state.snapshot(items).map(item => {
if (item.id === 1) {
return { ...item, name: 'Bob' };
}
return item;
});
}
</script>
4.3 延迟加载优化
<script>
// 懒加载组件
const HeavyComponent = $lazy(() => import('./HeavyComponent.svelte'));
let show = $state(false);
</script>
{#if show}
{@render HeavyComponent()}
{/if}
<button onclick={() => show = true}>Load Heavy Component</button>
五、Svelte 5 实战:构建一个 Todo 应用
5.1 项目初始化
# 创建 Svelte 5 项目
npm create svelte@latest my-app -- --template skeleton --types typescript
# 进入项目目录
cd my-app
# 安装依赖
npm install
# 启动开发服务器
npm run dev
5.2 创建 Todo 组件
<!-- src/lib/TodoItem.svelte -->
<script lang="ts">
let {
todo,
onToggle,
onDelete
}: {
todo: { id: number; text: string; completed: boolean };
onToggle: () => void;
onDelete: () => void;
} = $props();
</script>
<li class="todo-item" class:completed={todo.completed}>
<input
type="checkbox"
checked={todo.completed}
onchange={onToggle}
/>
<span class="text">{todo.text}</span>
<button class="delete" onclick={onDelete}>×</button>
</li>
<style>
.todo-item {
display: flex;
align-items: center;
padding: 8px;
border-bottom: 1px solid #eee;
}
.completed .text {
text-decoration: line-through;
color: #999;
}
.text {
flex: 1;
margin-left: 8px;
}
.delete {
background: none;
border: none;
color: #999;
cursor: pointer;
font-size: 18px;
}
.delete:hover {
color: #f00;
}
</style>
5.3 创建 Todo 列表
<!-- src/lib/TodoList.svelte -->
<script lang="ts">
import TodoItem from './TodoItem.svelte';
let todos = $state<Todo[]>([
{ id: 1, text: 'Learn Svelte 5', completed: false },
{ id: 2, text: 'Build a project', completed: false }
]);
let newTodoText = $state('');
// 派生状态:未完成的数量
let remainingCount = $derived(
todos.filter(t => !t.completed).length
);
// 添加 todo
function addTodo() {
if (newTodoText.trim()) {
todos = [...todos, {
id: Date.now(),
text: newTodoText.trim(),
completed: false
}];
newTodoText = '';
}
}
// 切换 todo 状态
function toggleTodo(id: number) {
todos = todos.map(t =>
t.id === id ? { ...t, completed: !t.completed } : t
);
}
// 删除 todo
function deleteTodo(id: number) {
todos = todos.filter(t => t.id !== id);
}
// 清除已完成
function clearCompleted() {
todos = todos.filter(t => !t.completed);
}
// 副作用:保存到 localStorage
$effect(() => {
localStorage.setItem('todos', JSON.stringify(todos));
});
</script>
<div class="todo-app">
<h1>Svelte 5 Todo App</h1>
<div class="add-form">
<input
type="text"
bind:value={newTodoText}
placeholder="What needs to be done?"
onkeydown={(e) => e.key === 'Enter' && addTodo()}
/>
<button onclick={addTodo}>Add</button>
</div>
<ul class="todo-list">
{#each todos as todo (todo.id)}
<TodoItem
{todo}
onToggle={() => toggleTodo(todo.id)}
onDelete={() => deleteTodo(todo.id)}
/>
{/each}
</ul>
<div class="footer">
<span>{remainingCount} item{remainingCount !== 1 ? 's' : ''} left</span>
<button onclick={clearCompleted}>Clear completed</button>
</div>
</div>
<style>
.todo-app {
max-width: 500px;
margin: 50px auto;
padding: 20px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
h1 {
text-align: center;
color: #333;
}
.add-form {
display: flex;
gap: 8px;
margin-bottom: 20px;
}
.add-form input {
flex: 1;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.add-form button {
padding: 8px 16px;
background: #4caf50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.todo-list {
list-style: none;
padding: 0;
margin: 0;
}
.footer {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 16px;
border-top: 1px solid #eee;
color: #666;
font-size: 14px;
}
.footer button {
background: none;
border: none;
color: #666;
cursor: pointer;
}
.footer button:hover {
text-decoration: underline;
}
</style>
六、Svelte 5 与其他框架对比
6.1 Svelte 5 vs React 19
| 特性 | React 19 | Svelte 5 | 优势方 |
|---|---|---|---|
| 响应式系统 | Hooks | Runes | 平手 |
| 包体积 | 45KB | 8KB | Svelte |
| 性能 | 高 | 非常高 | Svelte |
| 学习曲线 | 中等 | 较低 | Svelte |
| 生态成熟度 | 非常成熟 | 成熟 | React |
| TypeScript 支持 | 好 | 非常好 | Svelte |
| SSR 支持 | 强 | 强 | 平手 |
6.2 Svelte 5 vs Vue 3.5
| 特性 | Vue 3.5 | Svelte 5 | 优势方 |
|---|---|---|---|
| 响应式系统 | Proxy | Proxy | 平手 |
| 包体积 | 22KB | 8KB | Svelte |
| 性能 | 高 | 非常高 | Svelte |
| 学习曲线 | 低 | 较低 | 平手 |
| 生态成熟度 | 非常成熟 | 成熟 | Vue |
| TypeScript 支持 | 非常好 | 非常好 | 平手 |
| 文档质量 | 高 | 高 | 平手 |
七、Svelte 5 的未来路线图
7.1 预期特性
根据 Svelte 团队的规划,未来版本可能会带来:
- 更好的 Server Components 支持:类似 React Server Components
- 更强大的编译优化:进一步减小包体积
- 原生移动端支持:更好的跨平台开发体验
- AI 辅助开发:集成 AI 代码生成和优化建议
7.2 社区贡献指南
# 克隆 Svelte 仓库
git clone https://github.com/sveltejs/svelte.git
cd svelte
# 安装依赖
pnpm install
# 运行开发模式
pnpm dev
# 运行测试
pnpm test
# 构建
pnpm build
# 提交 PR
# https://github.com/sveltejs/svelte/pulls
八、总结
Svelte 5 的发布标志着前端框架进入了一个新的时代。Runes 系统的引入,让响应式编程更加直观和可控。显著的性能提升和更小的包体积,使得 Svelte 应用具有出色的用户体验。
对于开发者来说,Svelte 5 提供了:
- 更直观的响应式系统:Runes 让状态管理更清晰
- 更好的 TypeScript 支持:原生 TypeScript,无需额外配置
- 更小的包体积:8KB 的运行时,远小于其他框架
- 更好的性能:编译时优化让运行效率更高
- 平滑的迁移路径:提供了详细的迁移指南和工具
参考链接:
- Svelte 官方文档: https://svelte.dev/
- Svelte 5 Blog: https://svelte.dev/blog/svelte-5
- Svelte GitHub: https://github.com/sveltejs/svelte
- Svelte Society: https://sveltesociety.dev/
本文作者:程序员茄子 | 发布日期:2026-05-12