Svelte 5 Runes:從信號(Signal)出發的全新響應式系統
6 分鐘閱讀 1,000 字
Svelte 5 Runes:從信號(Signal)出發的全新響應式系統
Svelte 5 是一次意義深遠的重寫。儘管外表看起來像個版本號,它實際上代表了 Svelte 響應式模型的根本性變革——從基於編譯時靜態分析的方式,轉向以**信號(Signal)**為核心的 Runes API。
為什麼需要 Runes?
Svelte 4 的響應式系統依賴編譯器識別哪些變數是響應式的。這個方法在簡單場景下優雅,但有個關鍵限制:響應式邏輯無法跨越元件邊界抽取為可複用的模組。
// Svelte 4:響應式邏輯只能在元件內使用
// ❌ 這個函式沒有響應式特性
function createCounter() {
let count = 0; // 普通變數,不是響應式的
return { count, increment: () => count++ };
}Runes 讓響應式成為一等公民,可以在任何地方使用。
$state:宣告響應式狀態
<script>
// Svelte 4
let count = 0;
// Svelte 5 with Runes
let count = $state(0);
</script>
<button onclick={() => count++}>
點擊次數:{count}
</button>對物件和陣列,$state 會建立深層響應式代理:
<script>
let user = $state({
name: "Alice",
address: {
city: "台北"
}
});
// 深層屬性的修改也會觸發更新
function updateCity(city) {
user.address.city = city; // 觸發響應式更新
}
// 陣列操作同樣有響應式
let todos = $state([]);
function addTodo(text) {
todos.push({ text, done: false }); // push 也是響應式的
}
</script>$derived:計算屬性
<script>
let width = $state(10);
let height = $state(5);
// 當 width 或 height 改變時,area 自動重新計算
let area = $derived(width * height);
// 複雜計算使用 $derived.by
let stats = $derived.by(() => {
const total = todos.length;
const done = todos.filter(t => t.done).length;
return { total, done, pending: total - done };
});
</script>
<p>面積:{area}({width} × {height})</p>
<p>待辦:{stats.pending} / {stats.total}</p>$effect:副作用
<script>
let query = $state("");
let results = $state([]);
// 類似 React 的 useEffect,但不需要手動宣告依賴
$effect(() => {
if (query.length < 2) {
results = [];
return;
}
// query 改變時自動重新執行
fetch(`/api/search?q=${query}`)
.then(r => r.json())
.then(data => results = data);
// 清理函式(在下次執行前或元件銷毀時呼叫)
return () => {
// 取消進行中的 fetch
};
});
</script>重要:
$effect只在瀏覽器中執行,不在 SSR 時執行。
$props:元件 Props
<script>
// Svelte 4
export let name;
export let count = 0;
// Svelte 5
let { name, count = 0, onchange } = $props();
</script>接受所有 props(類似 Vue 的 v-bind):
<script>
let { class: className, style, ...restProps } = $props();
</script>
<div class={className} {style} {...restProps}>
<slot />
</div>可複用的響應式邏輯(Runes 的殺手級功能)
這是 Runes 最大的突破:響應式邏輯可以抽取到普通的 .svelte.ts 檔案中:
// counter.svelte.ts
export function createCounter(initial = 0) {
let count = $state(initial);
let doubled = $derived(count * 2);
function increment() { count++; }
function decrement() { count--; }
function reset() { count = initial; }
return {
get count() { return count; },
get doubled() { return doubled; },
increment,
decrement,
reset
};
}
// 可以在多個元件中使用
// ComponentA.svelte
const counter = createCounter(10);
// ComponentB.svelte
const counter = createCounter();更複雜的例子:全域狀態管理(不需要 store):
// stores/cart.svelte.ts
function createCartStore() {
let items = $state<CartItem[]>([]);
let total = $derived(
items.reduce((sum, item) => sum + item.price * item.quantity, 0)
);
let itemCount = $derived(
items.reduce((sum, item) => sum + item.quantity, 0)
);
return {
get items() { return items; },
get total() { return total; },
get itemCount() { return itemCount; },
addItem(product: Product) {
const existing = items.find(i => i.id === product.id);
if (existing) {
existing.quantity++;
} else {
items.push({ ...product, quantity: 1 });
}
},
removeItem(id: string) {
items = items.filter(i => i.id !== id);
}
};
}
// 單例模式:整個應用共用同一個購物車
export const cart = createCartStore();$bindable:雙向綁定 Props
<!-- TextInput.svelte -->
<script>
let { value = $bindable("") } = $props();
</script>
<input bind:value />
<!-- Parent.svelte -->
<script>
let text = $state("");
</script>
<TextInput bind:value={text} />
<p>你輸入了:{text}</p>Svelte 4 vs Svelte 5 對比
| 概念 | Svelte 4 | Svelte 5 |
|---|---|---|
| 響應式狀態 | let count = 0 |
let count = $state(0) |
| 計算屬性 | $: doubled = count * 2 |
let doubled = $derived(count * 2) |
| 副作用 | $: { ... } |
$effect(() => { ... }) |
| Props | export let name |
let { name } = $props() |
| 響應式跨元件 | 需要 store | 原生支援 |
| 細粒度更新 | 元件層級 | 屬性層級 |
遷移策略
Svelte 5 對 Svelte 4 的語法完全向後相容,可以逐步遷移:
- 升級到 Svelte 5,現有元件無需修改即可運行
- 新建元件直接使用 Runes
- 逐步將複雜邏輯重構為
.svelte.ts的可複用函式
小結
Svelte 5 的 Runes 是信號(Signal)理念在 Svelte 生態的完整實現,與 Solid.js、Angular Signals、Vue 的 Composition API 共享相似的設計哲學。最重要的突破是響應式邏輯的可攜性——它現在可以跨元件、跨檔案地被複用,這解決了 Svelte 4 最主要的痛點,讓大型應用的狀態管理變得更加優雅。
分享這篇文章