其實在很久之前我就注意到 Svelte,但一直沒把這個框架放在心上。
因為我之前的工作主要使用 Vue,但完全沒遇到過使用 Svelte 的項。
直到 Vite 的出現,我才開始開始重視 Svelte。
從 Vite 文檔 裡可以看到它支持這些模板:
能讓祖師爺也重視的框架,不簡單不簡單~
我喜歡用 Demo 的方式學習新技術,Svelte 官方入門教程 就提供了這種方式。
這是我覺得入門比較舒服且方便日後搜索的學習方式。
雖然 Svelte 官方入門教程 已經給出很多例子,而且 Svelte中文網 也有對應的翻譯,但有些翻譯看上去是機譯,而且部分案例可能不太適合新手學習~
本文的目的是把 Svelte 的學習流程梳理出來,讓第一次接觸 Svelte 的工友能順利上手。
本文適合人群:有HTML、CSS、JS基礎,知道並已經安裝了Node。
如果你是打算從0開始學習前端,那本文暫時還不適合你閱讀。
Svelte 簡介
Svelte 是一個構建web 應用程序的工具。
傳統框架如React 和Vue 在瀏覽器中需要做大量的工作,而Svelte 將這些工作放到構建應用程序的編譯階段來處理。
需要注意,Svelte 是一款編譯器。它可以將按照規定語法編寫的代碼打包成瀏覽器能運行的項目。
和其他前端框架一樣,同樣也是使用HTML、CSS 和 JavaScript 進行開發。
作者
在學習 Svelte 之前先了解一下它的父親(作者)。
Svelte 的作者叫 Rich Harris
可能國內大多數工友對他不是很熟悉(我也完全不熟),但應該聽過 Rollup。
沒錯,他也是 Rollup 的爸爸。
他在開發 Svelte 之前還開發過 Ractive.js,聽說 Vue 的部分實現也是受到了 Ractive 的啟發。
關於 Rich Harris 的介紹還有很多,我搜到的資料上這樣介紹到:
- 大學專業是學哲學的
- 在紐約時報調查組工作的圖形編輯,身兼記者和開發者職位
還有更多關於他和 Svelte 的介紹,可以看看《Svelte - The magical disappearing UI framework - Interview with Rich Harris》
Svelte 的優勢
Svelte 翻譯成中文就是 苗條 的意思,側面表明它打包出來的包非常小。
Svelte 主要優勢有以下幾點。
- 編譯器
在打開 Svelte官網 時就能看到這樣的介紹。
Svelte 是一種全新的構建用戶界面的方法。傳統框架如 React 和 Vue 在瀏覽器中需要做大量的工作,而 Svelte 將這些工作放到構建應用程序的編譯階段來處理。
Svelte 組件需要在 .svelte 後綴的文件中編寫,Svelte 會將編寫好的代碼翻編譯 JS 和 CSS 代碼。
- 打包體積更小
Svelte 在打包會將引用到的代碼打包起來,而沒引用過的代碼將會被過濾掉,打包時不會加入進來。
在 《A RealWorld Comparison of Front-End Frameworks with Benchmarks (2019 update)》 報告中,對主流框架進行了對比。

在經過 gzip 壓縮後生成的包大小,從報告中可以看出,Svelte 打包出來的體積甩開 Vue、React 和 Angular 幾條街。
這是因為經過 Svelte 編譯的代碼,僅保留引用到的部分。
- 不使用 Virtual DOM
Virtual DOM 就是 虛擬DOM,是用 JS 對象描述 DOM 節點的數據,由 React 團隊推廣出來的。虛擬DOM 是前端的網紅,因此也有很多開發者開始研究和搞辯論賽。
網上有一張圖對比了 Svelte 和 React 在數據驅動視圖的流程

其實主要對比了使用虛擬DOM和直接操作真實DOM的區別。 在 React 中實現數據驅動視圖大概流程是這樣的:
| |
Vue 的數據更新原理其實也差不多,只是實現方式和使用語法會有所不同。
diff算法 會根據數據更新前和更新後生成的虛擬DOM進行對比,只有兩個版本的虛擬DOM存在差異時,才會更新對應的真實DOM。
使用虛擬DOM對比的方式會比直接對比真實DOM的效率高。
而且真實DOM身上掛載的屬性和方法非常多,使用虛擬DOM的方式去描述DOM節點樹會顯得更輕便。
但這也意味著每次數據發生變化時都要先創建一個虛擬DOM,並使用 diff算法 將新虛擬DOM與舊虛擬DOM進行比對,這個步驟會消耗一點性能和需要一點執行時間。
而 Svelte 在未使用虛擬DOM的情況下實現了響應式設計。
我以粗暴的方式理解:Svelte 會監聽頂層組件所有變量,一旦某個變量發生變化,就更新使用過該變量的組件。這就僅僅只需更新受影響的那部分DOM元素,而不需要整個組件更新。
綜上所述,在我的理解力,虛擬DOM的思想很優秀,也是順應時代的產物,但虛擬DOM並不是最快的,JS 直接操作 DOM 才是最快。
《Virtual DOM is pure overhead》是 Svelte 官網上的一篇博客,專門討論虛擬DOM。有興趣的工友可以看看~
- 更自然的響應式
這裡說的響應式設計是只關於數據的響應,而不是像 Bootstrap 的響應式佈局。
現在流行的前端框架基本都使用 數據驅動視圖 這個概念,像 Vue 和 React 這些框架,都有響應式數據的概念。
但 Vue 和 React 在數據響應方面還是有點不那麼自然,我簡單舉幾個例子:
在 React 中,如果需要更新數據並在視圖中響應,需要使用 setState 方法更新數據。
在 Vue2 中,響應式數據要放在 data 裡,在 methods 中使用 this.xxx 來更新數據。
在 Vue3 的 Composition API 語法中,需要使用 ref 或者 reactive 等方法包裹數據,使用 xxx.value 等方式修改數據。
上面這幾種情況,感覺多少都添加了點東西才能實現響應式數據功能(至少在普通開發者開發時是這樣)。
在 Svelte 的理念中,響應式應該給開發者一種無感體驗,比如在 Excel 中,當我規定 C1 單元格的值是 A1 + B1 的和,設置好規則後,用戶只需要修改 A1 和 B1 即可,C1 會自動響應,而不需再做其他操作。

在這方面,Svelte 我認為在現階段是做得最自然的。

| |
上面的代碼中,1秒後修改 name 的值,並更新視圖。
從代碼就能看出,在使用 Svelte 開發項目時,開發者一般無需使用額外的方法就能做到和 Vue、React 的響應式效果。
如果你對 Svelte 響應式原理感興趣,推薦閱讀 FESKY 的 《Svelte 響應式原理剖析—— 重新思考Reactivity》
也可以看看 《Rethinking reactivity》,看看官方對 reactivity 的思考。
- 性能強
Stefan Krause 給出一份 性能測試報告(點擊可查看) 對比里多個熱門框架的性能。從 Svelte 的性能測試結果可以看出,Svelte 是相當優秀的。
- 內存優化
性能測試報告(點擊可查看) 也列出不同框架的內存佔用程度,Svelte 對內存的管理做到非常極致,佔用的內存也是非常小,這對於配置不高的設備來說是件好事。
第5、6點,由於測試報告比較長,我沒截圖放進文中。大家有興趣可以點開鏈接查看測試報告。
- 更關注無障礙體驗
在使用 Svelte 開發時會 自動對無障礙訪問方面的體驗進行檢測,比如 img 元素沒有添加 alt 屬性,Svelte 會向你發出一條警告。無障礙體驗對特殊人事來說是很有幫助的,比如當你在 img 標籤中設置好 alt 屬性值,使用有聲瀏覽器會把 alt 的內容讀出來。
在此我還要推薦2本關於設計體驗的書。
- 《點石成金:訪客至上的Web和移動可用性設計秘笈》
- 《包容性Web設計》
它們的封面長分別這個樣子


Svelte 的優勢肯定還有很多,但由於我開發經驗不足,只能總結出以上這些了。如果你對 Svelte 有更多理解,歡迎在評論區補充~
Svelte 的不足
- Svelte 對 IE 是非常不友好的,但我並不把這放在眼裡。如果想兼容 IE 我還是推薦使用 jQuery。
- Svelte 的生態不夠豐富。由於是新寵,生態方面肯定是不如 Vue 和 React 的。
與 Svelte 相關的庫
Sapper
Sapper 是構建在Svelte 上的框架,Sapper 提供了頁面路由、佈局模板、SSR等功能。
Svelte Native
Svelte Native 是建立在 NativeScript 之上的產物,可以開發安卓和iOS應用,是一個跨端技術。
有點類似於 React Native 和 Weex 之類的東西。
svelte-gl
svelte-gl 還沒正式發布,但這是個很有趣的工具,它和three.js類似,專門做3D應用的。
雖然現在 github 上的 Star 還不是很多,但也可以寫些 demo 玩玩。
創建項目
在開始之前,你需要在電腦上安裝Node環境(v16+)。
編輯工具我使用了 VS Code,同時安裝了 Svelte for VS Code 擴展插件。
使用 Svelte 前,必須有一個開發環境。
創建或使用開發環境有以下幾種方式:
REPLRollup版Webpack版Parcel版Vite版
本文使用的是 Vite 創建項目,但上面列出的所有方式我都會逐一說說。
REPL
REPL是 Svelte 提供的一個線上環境,打開 Svelte 官網 可以看到頂部導航欄上面有個 REPL 的選項。點擊該選項就可以跳轉到 Svelte 線上開發環境了。


REPL 是 read(讀取)、evaluate(執行)、print(打印) 和 loop(循環) 這幾個單詞的縮寫。
如果你只是想嘗試 Svelte 的某些功能或者測試小型代碼,可以使用這款線上工具。REPL還提供了多組件開發,按左上角的 + 可以創建新組件。組件的內容稍後會說到。
界面右側,頂部有3個選項:
Result: 運行結果。JS output: Svelte 編譯後的JS代碼。CSS output: Svelte 編譯後的CSS代碼。

在 REPL 界面右上角還有一個下載按鈕。

當你在線上環境寫好代碼,可以點擊下載按鈕把項目保存到本地,下載的文件是一個 zip,需要自己手動解壓。
然後使用以下命令初始化項目並運行即可。
| |
運行結果:

Rollup 版
Svelte 官方也提供了一個命令,可以下載 Svelte 項目到本地。
命令最後需要輸入你的項目名稱。
| |
運行結果:

這是官方提供的創建項目方式,這個項目是使用 Rollup 打包的。
Rollup 和 Svelte 都是同一個作者(Rich Harris)開發的,用回自家東西很正常。
Webpack 版
如果你不想使用 Rollup 打包項目,可以嘗試使用 Webpack。
| |
運行結果:

Parcel 版
我並 不推薦使用 該方法創建項目,因為Svelte 並沒有提供使用Parcel 打包工具的模板。但 GitHub 上有第三方的解決方案(點擊訪問倉庫)。
將DeMoorJasper/parcel-plugin-svelte的代碼下載下來。
| |
運行結果:

Vite 版
本文接下來所有例子都是使用 Vite 創建 Svelte 項目進行開發的。
使用 Vite 創建項目的原因是:快!
| |
運行結果:

本文使用 Vite 創建項目,目錄結構和Rollup版創建出來的項目結構稍微有點不同,但開發邏輯是一樣的。
起步
index.html、src/main.js 和 src/App.svelte 這三個是最主要的文件。index.html 是項目運行的入口文件,它裡面引用了 src/main.js 文件。src/main.js 裡引入了 src/App.svelte 組件,並使用以下代碼將 src/App.svelte 的內容渲染到 #app 元素裡。
| |
target 指明目標元素。
我們大部分代碼都是寫在 .svelte 後綴的文件裡。.svelte 文件主要確保 多個 HTML 元素、1個 script 元素 和 1個 style 元素。這3類元素都是可選的。
我們主要的工作目錄是 src 目錄。
為了減輕學習難度,我們先做這幾步操作。
- 清空全局樣式
如果你使用Rollup版創建項目,不需要做這一步。
在使用 Vite 創建的 Svelte 項目中,找到 src/app.css 文件,並把裡面的內容清空掉。
- 改造 src/App.svelte
將 src/App.svelte 文件改成以下內容
| |
此時點擊按鈕,頁面上的"雷猴" 就會變成"鯊魚辣椒"

上面的代碼其實和 Vue 有點像。
- 變量和方法都寫在
<script>標籤裡。 - 在
HTML中使用{}可以綁定變量和方法。 - 通過
on:click可以綁定點擊事件。
只需寫以上代碼,Svelte 就會自動幫我們做數據響應的操作。一旦數據發生改變,視圖也會自動改變。
是不是非常簡單!
基礎模板語法
Svelte 的模板語法其實和 Vue 是有點像的。如果你之前已經使用過 Vue,那本節學起來就非常簡單。
插值
在"起步章節" 已經使用過 插值 了。在 Svelte 中,使用 {} 大括號將 script 裡的數據綁定到 HTML 中。

| |
此時頁面上就會出現 name 的值。
這種語法和 Vue 是有點像的,Vue 使用雙大括號的方式 {{}} 綁定數據。 Svelte 就少一對括號。
表達式
在 HTML 中除了可以綁定變量外,還可以綁定表達式。

| |
屬性綁定
HTML 的屬性需要動態綁定數據時,也是使用 {} 語法。

| |
當鼠標放到 div 標籤上時,會出現 title 裡的提示信息。
渲染 HTML 標籤 @html
如果只是使用插值的方法渲染帶有 HTML 標籤的內容,Svelte 會自動轉義 <、> 之類的標籤。

| |
這種情況多數出現在渲染文本。
在 Vue 中有 v-html 方法,它可以將 HTML 標籤渲染出來。在 Svelte 中也有這個方法,在插值前面使用 @html 標記一下即可。

| |
但此方法有可能遭受 XSS 攻擊。
我在 「NodeJS 防止 xss 攻擊」 中簡單演示過 XSS 攻擊,有興趣的可以看看。
樣式綁定
在日常開發中,給 HTML 標籤設置樣式主要通過 行内 style 和 class 屬性。
基礎的 HTML 寫法和原生的一樣,這裡不過多講解。
下面主要講動態設置樣式,也就是將 JS 裡的變量或者表達式綁定到 style 或者 class 裡。
行內樣式 style

| |
1秒後,文字從紅色變成藍色。
綁定class

| |
在 HTML 裡可以使用 class:xxx 動態設置要激活的類。這裡的 xxx 是對應的類名。
語法是 class:xxx={state},當 state 為 true 時,這個樣式就會被激活使用。
條件渲染 #if
使用 {#if} 開頭、{/if} 結尾。
基礎條件判斷
| |
舉個例子

| |
1秒後改變狀態
兩種條件
| |
舉個例子

| |
多種條件
| |
舉個例子

| |
條件渲染的用法比較簡單,只要 JS 基礎就能看得懂。
列表渲染 #each
如果你有一堆數據需要展示出來,可以使用 #each 方法。
使用 {#each} 開頭、{/each} 結尾。
遍歷數組
| |
舉個例子

| |
要注意,Svelte 和 Vue 的遍歷在寫法上有點不同。
Vue 的方式是:
| |
Svelte 的方式是:
| |
遍歷數組(帶 index)

| |
注意:
as後面首先跟著元素,然後才是下標。而且元素和下標不需要用括號括起來。
如果元素是對象,可以解構

| |
默認內容
如果源數據沒有內容,是空數組的情況下,還可以組合 {:else} 一起使用。

| |
事件綁定 on:event
使用 on: 指令監聽 DOM 事件,on: 後面跟隨事件類型
語法:
| |
舉個例子,點擊按鈕時在控制台輸出"雷猴"。

| |
綁定其他事件(比如change等)也是同樣的道理。
事件修飾符
如果你只希望某些事件只執行一次,或者取消默認行為,或者阻止冒泡等,可以使用事件修飾符。
語法:
| |
舉個例子,我希望點擊事件只能執行一次,之後再點擊都無效,可以使用官方提供的 once 修飾符。

| |
從上圖可以看出,多次點擊都只是輸出1次"雷猴"。
除了 once 之外,還有以下這些修飾符可以用:
preventDefault:禁止默認事件。在程序運行之前調用event.preventDefault()stopPropagation:調用event.stopPropagation(), 防止事件到達下一個標籤passive:改善了touch/wheel事件的滾動表現(Svelte會在合適的地方自動加上它)capture:表示在capture階段而不是bubbling觸發其程序once:程序運行一次後刪除自身
串聯修飾符
修飾符還可以串聯起來使用,比如 on:click|once|capture={...}
但需要注意,有些特殊的標籤使用修飾符會出現"意想不到“的結果,比如 <a> 標籤。

| |
本來是想給 <a> 標籤綁定一個點擊事件,第一次點擊時在控制台輸出一句話,並且禁止 <a> 標籤的默認事件。
所以使用了 once 和 preventDefault 修飾符。
但實際上並非如此。上面的代碼意思是 once 設定了只執行一次 toLearn 事件,並且只有一次 preventDefault 是有效的。
只有點擊時就不觸發 toLearn 了,而且 preventDefault 也會失效。所以再次點擊時,<a>元素就會觸發自身的跳轉功能。
數據綁定 bind
數據綁定通常會和表單元素結合使用。bind 可以做到雙向數據綁定的效果。我覺得 Svelte 裡的 bind 有點像 Vue 的 v-model。
語法:
| |
input 單行輸入框

| |
如果只是使用 value={msg} 的寫法,input 默認值是 hello,當輸入框的值發生改變時,並沒有把內容反應回 msg 變量裡。
此時就需要使用 bind 了。

| |
textarea 多行文本框
多行文本框同樣綁定在 value 屬性上。

| |
input range 範圍選擇
因為都是 input 元素,只是 type 不同而已。所以範圍選擇元素同樣需要綁定value。

| |
radio 單選
單選框通常是成組出現的,所以要綁定一個特殊的值 bind:grout={variable}

| |
checkbox 複選框

| |
select 選擇器

| |
select multiple 選擇器

| |
簡寫形式
如果 bind 綁定的屬性和在 JS 裡聲明的變量名相同,那可以直接綁定

| |
這個例子中, bind:value 綁定的屬性是 value,而在 JS 中聲明的變量名也叫 value,此時就可以使用簡寫的方式。
$: 聲明反應性
通過使用
$:JS label 語法作為前綴。可以讓任何位於top-level 的語句(即不在塊或函數內部)具有反應性。每當它們依賴的值發生更改時,它們都會在 component 更新之前立即運行。
上面這段解釋是官方文檔的解釋。$:在文檔中稱為Reactivity,中文文檔成它為反應性能力。
但我使用 $: 時,覺得這個功能有點像 Vue 的 computed。$: 可以監聽表達式內部的變化從而做出響應。

| |
使用 $: 聲明的 double 會自動根據 count 的值改變而改變。
如果將以上代碼中 $: 改成 let 或者 var 聲明 count,那麼 count 將失去響應性。
這樣看來,真的和 Vue 的 computed 的作用有那麼一點像。
異步渲染 #await
Svelte 提供異步渲染標籤,可以提升用戶體驗。
語法:
| |
以 #await 開始、以 /await 結束。:then 代表成功結果,:catch 代表失敗結果。expression是判斷體,要求返回一個Promise。
其實用法和 #if ... :else if ... /if 有那麼一丟丟像。
舉個例子

| |
如果將上面的 resolve 改成 reject 就會走 :catch 分支。
基礎組件
在 Svelte 中,創建組件只需要創建一個 .svelte 為後綴的文件即可。
通過 import 引入子組件。
比如,在 src 目錄下有 App.svelte 和 Phone.svelte 兩個組件。App.svelte 是父級,想要引入 Phone.svelte 並在 HTML 中使用。

App.svelte
| |
Phone.svelte
| |
組件通訊
組件通訊主要是 父子組件 之間的數據來往。
父傳子
比如上面的例子,手機號希望從 App.svelte 組件往 Phone.svelte 裡傳。
可以在 Phone.svelte 中聲明一個變量,並公開該變量。App.svelte 就可以使用對應的屬性把值傳入。

App.svelte
| |
Phone.svelte
| |
如果此時 App.svelte 組件沒有傳值進來,Phone.svelte 就會使用默認值。
子傳父
如果想在子組件中修改父組件的內容,需要把修改的方法定義在父組件中,並把該方法傳給子組件調用。
同時需要在子組件中引入 createEventDispatcher 方法。

App.svelte
| |
Phone.svelte
| |
父組件接受參數是一個對象,子組件傳過來的值都會放在 detail 屬性裡。
插槽 slot
和 Vue 一樣,Svelte 也有組件插槽。
在子組件中使用 <slot> 標籤,可以接收父組件傳進來的 HTML 內容。

App.svelte
| |
Phone.svelte
| |
生命週期
生命週期是指項目運行時,指定時期會自動執行的方法。
Svelte 中主要有以下幾個生命週期:
onMount: 組件掛載時調用。onDestroy: 組件銷毀時執行。beforeUpdate: 在數據更新前執行。afterUpdate: 在數據更新完成後執行。tick: DOM元素更新完成後執行。
以上生命週期都是需要從 svelte 裡引入的。
用 onMount 舉個例子

| |
在組件加載完1秒後,改變 title 的值。
onDestroy、beforeUpdate 和 afterUpdate 都和 onMount 的用法差不多,只是執行的時間條件不同。
tick 是比較特殊的,tick 和 Vue 的 nextTick 差不多。
在 Svelte 中,tick 的使用語法如下:
| |
總結
本文主要講解了 Svelte 的基礎用法,但 Svelte 的內容和 API 遠不止此。它還有很多高級的用法以及提供了過渡動畫功能等。
Svelte 是一個 Web 應用的構建工具,它打包出來的項目體積比較小,性能強,不使用虛擬DOM。
但 Svelte 的兼容性和周邊生態相比起 Vue 和 React 會差一點。
所以日常項目中需要根據 Svelte 的優缺點進行取捨。
