为什么要有 Runes
Svelte 4 的响应式是“编译器魔法”:给变量赋值就自动更新,$: 声明派生值。这套写法新手友好,但在组件变大后会暴露问题——
- 只有顶层
let赋值才触发更新,函数内部修改数组/对象需要额外处理; $:的依赖分析靠静态编译,跨文件、跨函数时容易失效;- 无法把响应式逻辑抽到
.js文件复用。
Runes(符文) 是 Svelte 5 给出的答案:用显式的 $state、$derived、$effect 函数标记响应式,规则统一、可跨文件、可推理。
三大核心 Runes
$state:声明响应式状态
<script>
let count = $state(0);
const user = $state({ name: "Ada", tags: ["math"] });
function inc() {
count += 1;
// 深层属性修改同样触发更新(基于 Proxy)
user.tags.push("logic");
}
</script>
<button onclick={inc}>{count} · {user.tags.length}</button>不再需要 svelte/store 的 $store 语法糖,也不再需要 $: 去声明派生。
$derived:声明派生值
<script>
let price = $state(10);
let qty = $state(2);
// 自动追踪依赖,同步计算
let total = $derived(price * qty);
// 复杂逻辑用 $derived.by
let label = $derived.by(() => {
const t = price * qty;
return t > 100 ? `大额 ${t}` : `小计 ${t}`;
});
</script>
<p>{total} — {label}</p>$derived 是惰性求值的,且不会被重复触发——这一点比 $: 更可控。
$effect:副作用
<script>
let query = $state("");
let results = $state([]);
$effect(() => {
const controller = new AbortController();
fetch(`/api/search?q=${query}`, { signal: controller.signal })
.then((r) => r.json())
.then((data) => (results = data));
// 清理函数:下次执行或销毁时调用
return () => controller.abort();
});
</script>
<input bind:value={query} />
<ul>{#each results as r}<li>{r.title}</li>{/each}</ul>注意:$effect 只在客户端运行,服务端渲染时会被跳过。能用 $derived 表达的,就不要写 $effect。
跨文件复用:.svelte.js
Runes 最大的工程价值是响应式逻辑可以放进普通 JS 模块:
// store.svelte.js —— 文件名必须含 .svelte.
function createCart() {
let items = $state([]);
return {
get items() {
return items;
},
get total() {
return items.reduce((s, i) => s + i.price * i.qty, 0);
},
add(item) {
const found = items.find((i) => i.id === item.id);
if (found) found.qty += 1;
else items.push({ ...item, qty: 1 });
},
remove(id) {
items = items.filter((i) => i.id !== id);
},
};
}
export const cart = createCart();<script>
import { cart } from "./store.svelte.js";
</script>
<button onclick={() => cart.add({ id: 1, price: 9.9 })}>
加入购物车(共 {cart.total})
</button>从 Svelte 4 迁移
| Svelte 4 | Svelte 5 |
|---|---|
let x = 0 | let x = $state(0) |
$: y = x * 2 | let y = $derived(x * 2) |
$: { sideEffect() } | $effect(() => sideEffect()) |
export let prop | let { prop } = $props() |
createEventDispatcher | let { onEvent } = $props() 回调 |
on:click | onclick |
# 官方迁移工具自动改写大部分写法
npx svelte-migrate svelte-5性能与体验
Svelte 5 把内部实现换成了细粒度信号(signals),更新不再以组件为单位重跑,而是精确到绑定。官方 benchmark 中,大型列表的更新耗时下降明显,同时包体积依旧显著小于 React + 运行时的组合。
小结
Runes 让 Svelte 从“靠编译器猜意图”变成“开发者显式声明意图”。代价是要多敲几个字符,收益是可预测、可复用、可扩展——对中大型项目而言,这笔交易非常划算。
评论 0
还没有评论,来说两句吧。