其實在很久之前我就注意到 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 前,必須有一個開發環境。
創建或使用開發環境有以下幾種方式:
REPL
Rollup
版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 的優缺點進行取捨。