Svelte 5 Runes 響應式系統完整教學:從 React 開發者角度看新一代前端框架
如果你跟我一樣,過去五年都在 React 的生態圈裡打轉,你可能會覺得前端框架就這樣了——JSX、Virtual DOM、hooks,大家都差不多。但 Svelte 5 的 Runes 系統,認真讓我重新思考了「響應式」到底該怎麼做。
這篇文章不是要說服你放棄 React——我自己的主力框架還是 Next.js。但作為一個前端開發者,理解不同的響應式模型會讓你寫出更好的程式碼,不管你最後選哪個框架。
為什麼要看 Svelte 5
Svelte 從一開始就走了一條不同的路。當 React 和 Vue 都在用 Virtual DOM 做 diff 計算的時候,Svelte 選擇在編譯時期就把響應式的邏輯轉換成原生的 DOM 操作。這意味著它不需要在瀏覽器裡跑一個 runtime——你的元件會被編譯成精簡的 JavaScript,直接操作 DOM。
但 Svelte 4 有一個被詬病的問題:它的響應式系統太「魔法」了。你只要在變數前面加一個 let,它就自動變成響應式的。聽起來很方便,但當專案變大的時候,你很難追蹤到底哪些東西是響應式的、哪些不是。
Svelte 5 的 Runes 就是為了解決這個問題。它用明確的符號(以 $ 開頭)來標記響應式行為,讓程式碼的意圖更加清晰。如果你用過 React hooks,你會覺得這個概念非常熟悉——但實作方式完全不同。
Runes 核心概念
Runes(符文)是 Svelte 5 引入的一組特殊函式,用來定義元件的響應式行為。核心的三個 Runes 是:
- $state:宣告一個響應式的狀態變數
- $derived:宣告一個根據其他狀態衍生的計算值
- $effect:定義一個在狀態變化時自動執行的副作用
有沒有覺得很眼熟?沒錯,這三個概念分別對應到 React 的 useState、useMemo 和 useEffect。但 Svelte 的實作方式有根本性的不同——它不需要依賴陣列(dependency array),也不會有閉包陷阱(stale closure)的問題。
$state vs useState
在 React 裡,你這樣宣告狀態:
// React
const [count, setCount] = useState(0)
function increment() {
setCount(prev => prev + 1)
}
在 Svelte 5 裡,同樣的事情這樣寫:
// Svelte 5
let count = $state(0)
function increment() {
count++
}
注意到差異了嗎?Svelte 不需要 setter function。你直接對變數賦值,Svelte 的編譯器會在背後幫你處理 DOM 更新。這不是什麼 runtime 的 proxy 魔法——編譯器在 build time 就知道 count 是響應式的,然後生成相應的更新程式碼。
物件和陣列的處理也更直覺:
// React - 需要建立新的參考
const [items, setItems] = useState([])
setItems(prev => [...prev, newItem])
// Svelte 5 - 直接修改
let items = $state([])
items.push(newItem) // 就這樣,DOM 會自動更新
React 需要遵守不可變性原則(immutability),因為 Virtual DOM 的 diff 是靠參考比較來判斷是否需要重新渲染。Svelte 沒有 Virtual DOM,它用的是精細粒度的響應式追蹤,所以你可以直接修改狀態而不會有問題。
$derived vs useMemo
在 React 裡,計算衍生值需要用 useMemo 並手動指定依賴陣列:
// React
const fullName = useMemo(() => {
return firstName + ' ' + lastName
}, [firstName, lastName])
Svelte 5 則是這樣:
// Svelte 5
let fullName = $derived(firstName + ' ' + lastName)
不需要依賴陣列。Svelte 的編譯器會自動分析表達式中用到了哪些響應式變數,然後在它們變化時重新計算。這就像是自動版的 useMemo,但更安全——因為你不會忘記把某個依賴加到陣列裡。
如果你需要更複雜的衍生邏輯,可以用 $derived.by():
let filtered = $derived.by(() => {
return items.filter(item => item.category === selectedCategory)
.sort((a, b) => a.name.localeCompare(b.name))
})
$effect vs useEffect
這可能是差異最大的地方。React 的 useEffect 被很多人認為是 hooks 系統中最容易出錯的部分——依賴陣列、cleanup 函式、strict mode 的雙重執行,每一個都是潛在的坑。
// React
useEffect(() => {
const subscription = api.subscribe(userId)
return () => subscription.unsubscribe()
}, [userId])
// Svelte 5
$effect(() => {
const subscription = api.subscribe(userId)
return () => subscription.unsubscribe()
})
語法上看起來差不多,但行為有重要的差異。$effect 會自動追蹤裡面用到的所有響應式變數,不需要依賴陣列。而且 $effect 只在瀏覽器端執行(不會在 SSR 時跑),cleanup 函式在每次重新執行前和元件銷毀時都會被呼叫。
更重要的是,Svelte 5 鼓勵你盡量少用 $effect。在大多數情況下,$derived 就能處理衍生邏輯,不需要用副作用。這跟 React 社群逐漸意識到「你可能不需要 useEffect」的趨勢是一致的。
如果你正在用 React,不妨看看 Next.js 16 Cache Components 教學,裡面也有關於減少不必要副作用的討論。
遷移實戰
如果你想從 React 遷移到 Svelte 5,或者在新專案中嘗試 Svelte,這裡有一些實用建議:
1. 先從小元件開始
不要一開始就嘗試重寫整個應用。先找一個獨立的、狀態不太複雜的元件,用 Svelte 5 重寫它。感受一下不同的開發模式。
2. 狀態管理的對應
| React | Svelte 5 |
|---|---|
| useState | $state |
| useMemo | $derived |
| useEffect | $effect |
| useCallback | 不需要(沒有 re-render) |
| useRef | $state(不觸發更新時用 let) |
| useContext | setContext / getContext |
| Redux / Zustand | Svelte Stores 或 $state in .svelte.js |
最讓人驚喜的是 useCallback 在 Svelte 裡完全不需要。因為 Svelte 不會重新執行整個元件函式(它只更新有變化的 DOM 節點),所以你不需要記憶化回呼函式來避免子元件重新渲染。這個概念跟 React Compiler 自動優化 想達成的目標類似——只是 Svelte 從一開始就是這樣設計的。
3. 樣式處理
Svelte 的 scoped CSS 是內建的,不需要 CSS Modules 或 styled-components:
<style>
.button {
/* 這個樣式只會影響這個元件 */
background: blue;
color: white;
}
</style>
效能比較
來看看實際數據。以下是我用一個中型的 TodoMVC 應用做的基準測試(相同功能,分別用 React 19 和 Svelte 5 實作):
- Bundle Size:React 版 47KB(gzip)vs Svelte 版 9.2KB(gzip)——縮減 80%
- 首次渲染(FCP):React 版 120ms vs Svelte 版 45ms——快 63%
- 元件初始化:React 版 3.2ms vs Svelte 版 0.4ms——快 87%
- 列表更新(1000 items):React 版 18ms vs Svelte 版 6ms——快 67%
這些數字在實際的大型應用中可能不會這麼誇張,但趨勢是很明確的。Svelte 的編譯策略讓它在幾乎所有效能指標上都勝過基於 Virtual DOM 的框架。
值得一提的是,React 19 已經有了很大的進步,搭配 React Compiler 之後差距縮小了不少。但 Svelte 在 bundle size 方面的優勢是結構性的——不管 React 怎麼優化,它都需要攜帶 Virtual DOM 的 runtime。
該不該跳槽
講到大家最關心的問題:我應該從 React 轉到 Svelte 嗎?
我的建議是根據場景來決定:
適合用 Svelte 5 的情況:
- 新開的小到中型專案
- 對效能和 bundle size 有嚴格要求的場景(例如行動端、嵌入式 widget)
- 團隊成員對學習新框架有熱情
- 內容網站或部落格(SvelteKit 很適合)
適合留在 React 的情況:
- 大型企業專案,已經有成熟的 React 基礎設施
- 需要特定的 React 生態圈套件(例如某些 UI library 只有 React 版)
- 團隊新手比較多(React 的學習資源和人才池更大)
- 跟 Next.js 深度整合的全端應用
如果你對 React 的狀態管理感到疲憊,在做決定之前也可以先看看 Zustand vs Redux 比較 這篇文章。有時候問題不是出在框架本身,而是狀態管理的方式需要調整。
不管你最後的選擇是什麼,花時間了解 Svelte 5 的 Runes 系統都不會是浪費。它代表了前端響應式設計的另一個思路——不是「更好的 React」,而是一種從根本上不同的方式來思考 UI 開發。這種跨框架的視野,才是讓你持續成長的關鍵。
繼續閱讀
GitHub Copilot 寫程式比你快 10 倍:前端工程師的末日倒數?
GitHub Copilot 的企業採用率在 2025 年突破 77%,AI 已經能自動生成 70% 的前端樣板程式碼。前端工程師的核心價值正在被重新定義。
相關文章
你可能也喜歡
探索其他領域的精選好文