因此,Svelte v5,可能是現有最好的前端框架的最新版本已經發布,它與之前的版本有很大不同。主要差異在於其核心:如何實現變數的反應性。由於這些變化,Svelte 變得更容易,同時也變得更困難。
由於自v5@next.155 以來我一直在現實世界的微前端專案中與Svelte v5 密切合作,我決定撰寫本系列文章來傳遞我所獲得的知識來幫助您理解、接受並有可能將您的程式碼遷移到Svelte v5。
Svelte v4 的反應系統簡直就是藝術品:Svelte 靜態分析元件中的程式碼,然後產生在常規 JavaScript 變數發生變化時強制更改 DOM 的程式碼。簡單、優雅且性能非常好。一個簡單的例子:
<script lang="ts"> let clickCount = 0; function countClicks() { ++clickCount; } </script> <svelte:document on:click={() => countClicks()} /> <pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}
這個簡單的元件為文件物件新增了一個「click」事件監聽器並計算點擊次數。只需上面的程式碼就可以即時顯示點擊次數。太棒了,對吧?
就像生活中的一切一樣,這並不完美。 Rich Harris(Svelte 的創作者)已經解釋了這些注意事項,我不會在本文中詳細介紹這些內容。我只提一個:程式碼重構。
更重要的警告之一是無法將該反應系統帶到組件之外。例如,我們無法建立一個可重複使用的模組來封裝範例中 countClicks 函數的實作。
由於反應性取決於靜態分析,因此將函數移走並放入模組中會將變數突變隱藏到靜態程式碼分析器中,然後反應性就會遺失。
符文一詞指的是“魔法符號”,Svelte 採用這個術語來命名以下功能外觀“魔法”術語:
$州
$道具
$可綁定
$衍生
$效果
Svelte v5 中的反應性是由這些符文的使用所控制。
值得注意的是,雖然它們看起來像 R 值,但它們產生的程式碼實際上是 L 值。換句話說,不要認為你可以在變數之間傳遞狀態。下面的 $state rune 部分對此進行了詳細介紹。
這種新反應系統的主要優點是:
能夠在組件之外重構反應式程式碼
細粒反應性
前者意味著我們可以在元件之外擁有響應式變數;後者意味著元件重新渲染在對狀態變化做出反應時更有針對性。
本文未介紹在組件外部擁有狀態的能力,但請關注本系列,因為將會有一篇關於此的文章。
另一方面,細粒度反應性意味著Svelte 現在可以知道狀態物件中的哪個屬性發生了變化,並且僅重新渲染(並重新運行效果並重新計算派生值)受該特定屬性影響的內容僅有的。在某些情況下,這是一個重大的效能改進。舉個簡單的例子:如果一個大型表格元件看到一個新行加入到其資料中,Svelte 將只渲染該新行。如果第三行中的單一儲存格值發生更改,則只有顯示該值的儲存格才會重新渲染。現在清楚了嗎?希望是這樣,但如果不是,請在評論部分打我。
這個符文用來建立反應狀態。讓我們重寫上面的 Svelte v4 程式碼範例:
<script lang="ts"> let clickCount = 0; function countClicks() { ++clickCount; } </script> <svelte:document on:click={() => countClicks()} /> <pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}
為了達到與 Svelte v4 相同的結果,我們只需使用 $state(0) 而不是 0。
控制這個符文的主要規則是它只能用於初始化變數或類別字段,這與您剛才讀到的重要註釋有關:符文在語法上看起來像函數,但事實並非如此。編譯器用與函數功能(計算並傳回值)的想法不相容的程式碼取代符文。這表示以下不會建立第二個反應變數:
<script lang="ts"> let clickCount = $state(0); function countClicks() { ++clickCount; } </script> <svelte:document onclick={() => countClicks()} /> <pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}
clickCount 的反應性質不會透過使用賦值運算子轉移或複製到 secondaryClickCount。如果符文是函數,那麼上面的程式碼就可以工作,但它們不是。
關於 $state 還有一件更重要的事情要提及:它使其值具有深度反應性。這意味著如果該值是屬性包含對象的對象,那麼所包含對象的屬性也是反應性的。這種模式遞歸地應用,因此整個物件圖最終都是反應性的。
元件屬性預計是反應性的,Svelte v5 使用 $props rune 實現了這一點。
<script lang="ts"> let clickCount = $state(0); let secondClickCount = clickCount; function countClicks() { ++clickCount; } </script>
因為使用 TypeScript 是專案的一切,所以我們先使用型別宣告元件屬性。可選屬性透過附加 ? 進行標記。正如它的名字一樣。
然後是符文的使用,這是一種解構語句。此範例展示如何指派預設值以及如何允許「其餘」屬性(即任何其他屬性)。此元件將屬性的「其餘」部分作為屬性(屬性)傳播(套用)到 span HTML 元素上。
你可以讓 props: Props = $props();定義屬性並且它可以工作,但是您無法指定各種屬性的預設值,因此我建議您始終按所示聲明屬性。順便說一下,如果不解構的話,我也不知道該如何聲明restProperties。
如果您注意的話,上面的程式碼會產生 TypeScript 錯誤。畢竟,Props 類型沒有提及任何「休息」屬性。我們如何輸入restProps?
一般來說,您可以執行以下操作來允許所有類型的操作。我想這取決於你的 TypeScript 技能。
以下開啟 Props 類型以允許任何 data-* 屬性:
<script lang="ts"> let clickCount = 0; function countClicks() { ++clickCount; } </script> <svelte:document on:click={() => countClicks()} /> <pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}
這個允許任何事:
<script lang="ts"> let clickCount = $state(0); function countClicks() { ++clickCount; } </script> <svelte:document onclick={() => countClicks()} /> <pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}
但通常情況下,需要允許接收 restProps 的 HTML 元素的屬性,在我們的範例中,這就是 span HTML 元素。
對於這個常見場景,Svelte v5 提供了應該覆蓋大部分 HTML 元素的類型:
<script lang="ts"> let clickCount = $state(0); let secondClickCount = clickCount; function countClicks() { ++clickCount; } </script>
使用後者將使 VS Code 等 GUI 為 span HTML 元素的可能的 props(屬性)提供準確的 Intellisense。不錯吧?
HTMLAttributes
;介面用於屬性清單中沒有特殊性的 HTML 元素。然而,許多元素確實有。例如,不要執行 HTMLAttributes ,而是從 'svelte/elements' 匯入 HTMLButtonAttributes 介面。
最後一個細節是預設值。其實沒什麼好說的,例子已經說明了一切:操作屬性的預設值是「sum」。如果在使用元件時未指定該屬性,則該屬性將採用該值。
如果所需的預設值未定義,則完全不要指定任何內容。
這是一個非常特殊的符文,只能在元件屬性中使用。它將屬性標記為可綁定。
如果您不知道或不記得,Svelte 允許屬性的 2 路綁定。 Vue 也有這個功能,而相較之下,React 沒有。
使用方法超簡單:
<script lang="ts"> type Props = { data: number[]; operation?: 'sum', 'avg'; }; let { data, operation = 'sum', ...restProps, }: Props = $props(); function sum() { return data.reduce((p, c) => p + c); } function avg() { return sum() / data.length } </script> <span class="amount" {...restProps}>{operation === 'sum' ? sum() : avg()}</span> <style> .amount { font-family: monospace; } </style>
始終將其值修改為可綁定的屬性,否則 Svelte 會發出控制台警告。此警告指出元件不應修改不屬於它們的狀態,如果有意這樣做,則應使用綁定。
如範例所示,可以透過 $bindable rune 指定屬性預設值。此範例將屬性的預設值設為 5。
但是預設值在這裡有意義嗎?嗯,是的。將屬性聲明為可綁定並不意味著它是必需的。
每當我們需要使用 props 或其他反應狀態(可能隨時間變化)的值來計算值時,我們都會使用 $衍生符文。
將計算總和和平均值的範例元件帶回來,我們可以用這個符文重寫它:
<script lang="ts"> let clickCount = 0; function countClicks() { ++clickCount; } </script> <svelte:document on:click={() => countClicks()} /> <pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}
現在我們有一個名為 result 的新變量,它與其輸入一樣具有反應性,並且每次資料數組中的資料發生變化時都會自動重新計算。它本身是一個反應變量,因此使用它的模板(組件的 HTML 部分)也會更新。
這個符文允許我們指定在反應資料發生變化時執行的任意程式碼。為了讓這個符文發揮其魔力,它會追蹤在執行過程中讀取的反應資料。然後,每當庫存中的任何內容改變其值時,都會使用此反應資料庫存來重新觸發效果。
最常見的場景可能是根據更改的值重新觸發資料獲取操作:
<script lang="ts"> let clickCount = $state(0); function countClicks() { ++clickCount; } </script> <svelte:document onclick={() => countClicks()} /> <pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}
每當我們不希望 $衍生變數持有承諾時,非同步操作通常是內部效果的常態。但就我個人而言,由於在 Svelte 中使用 Promise 非常容易,所以我會簡單地使用 $driven 值。接下來顯示的變體使數據成為具有承諾的反應式計算值:
<script lang="ts"> let clickCount = $state(0); let secondClickCount = clickCount; function countClicks() { ++clickCount; } </script>
一般來說,如果您將 $state 和 $effect 結合起來,那麼使用 $driven 可能會更好。不過,這條規則也有例外,所以請將其視為經驗法則,而不是聖言。
如果資料取得不是 $effect 的一個很好的例子,那麼什麼才是呢?我們來看看這個:
<script lang="ts"> type Props = { data: number[]; operation?: 'sum', 'avg'; }; let { data, operation = 'sum', ...restProps, }: Props = $props(); function sum() { return data.reduce((p, c) => p + c); } function avg() { return sum() / data.length } </script> <span class="amount" {...restProps}>{operation === 'sum' ? sum() : avg()}</span> <style> .amount { font-family: monospace; } </style>
這是一個簡單的計時器元件,透過其狀態屬性進行控制。這裡使用 $effect 符文來強制計時器的操作。你能想像將其重構為 $衍生嗎?順便說一下,不要嘗試,因為 elapsed 是 prop,所以它不能同時是 $driven 和 prop。
Svelte v5 配備了全新的反應性引擎,旨在實現更好的重新渲染效能和更好的程式碼重構。使用新的反應性系統既簡單又複雜:簡單是因為系統的設計很好地涵蓋了常見場景,而困難一點是因為與 v4 相比,程式碼變得更加複雜。
無論如何,新系統功能強大,能夠優雅而有效地滿足大多數場景,為所有可能的可能性提供符文,並且易於使用,儘管一開始有點奇怪。
本文僅介紹了符文的介紹部分,以及一些個人使用符文的經驗。還有更多主題可以幫助您(讀者朋友)更快掌握 Svelte 的新版本,即:
深入了解 $effect 的工作原理
高階符文($state.raw、$categories.by、$effect.pre 等)
用反應狀態取代商店
異常狀況
看看這些基準測試結果:互動式結果 (krausest.github.io)
現在,框架清單令人震驚,因此您可以複製以下 JSON,然後使用「貼上」按鈕將其貼上到網頁中(請參閱螢幕截圖):
<script lang="ts"> let clickCount = 0; function countClicks() { ++clickCount; } </script> <svelte:document on:click={() => countClicks()} /> <pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}
順便說一句,我認為簡單地聚焦視窗並透過鍵盤貼上也可以。
這將框架清單縮小到更流行的框架,或至少是我認為流行的框架。也許你比我更了解。
遺憾的是,Svelte v4 不再出現在圖表中,但正如您所看到的,在選定的框架中,前3 名是無可爭議的:Vanilla JS、Solid和Svelte。
另一方面,看到 React v19 表現如此糟糕令人難過。編譯器不是應該讓它變得更好嗎?看來最後的努力都是白費了。當然,它似乎優於 React v18,但僅此而已。我不確定為什麼 Meta 繼續在 React 上投資。大家有什麼想法嗎?
以上是學習新的 Svelte veactivity 系統的詳細內容。更多資訊請關注PHP中文網其他相關文章!