前端新寵 Svelte 帶來哪些新思想?趕緊學起來!

語言: CN / TW / HK

Svelte[1] 是我用過最爽的框架,就算 Vue 和 React 再強大,生態再好,我還是更喜歡 Svelte,因為它開發起來真的很爽。

其實在很久之前我就注意到 Svelte[2] ,但一直沒把這個框架放在心上。

因為我之前的工作主要使用 Vue,偶爾也會接觸到一些 React 專案,但完全沒遇到過使用 Svelte 的項。

直到 Vite 的出現,我才開始開始重視 Svelte。

從 Vite文件[3] 裡可以看到它支援這些模板:

JavaScript

TypeScript

vanilla [4]

vanilla-ts [5]

vue [6]

vue-ts [7]

react [8]

react-ts [9]

preact [10]

preact-ts [11]

lit [12]

lit-ts [13]

svelte [14]

svelte-ts [15]

能讓祖師爺也重視的框架,不簡單不簡單~

我喜歡用 Demo 的方式學習新技術,Svelte 官方入門教程[16] 就提供了這種方式。

這是我覺得入門比較舒服且方便日後搜尋的學習方式。

雖然 Svelte 官方入門教程[17] 已經給出很多例子,而且 Svelte中文網[18] 也有對應的翻譯,但有些翻譯看上去是機譯,而且部分案例可能不太適合新手學習~

本文的目的是把 Svelte 的學習流程梳理出來,讓第一次接觸 Svelte 的工友能順利上手。

**本文適合人群:有 HTML 、CSS 、JS 基礎,知道並已經安裝了 Node**。

如果你是打算從0開始學習前端,那本文暫時還不適合你閱讀。

Svelte 簡介

  • Svelte[19] 是一個構建 web 應用程式的工具。
  • 傳統框架如 React 和 Vue 在瀏覽器中需要做大量的工作,而 Svelte 將這些工作放到構建應用程式的編譯階段來處理。

需要注意,Svelte 是一款編譯器。它可以將按照規定語法編寫的程式碼打包成瀏覽器能執行的專案。

和其他前端框架一樣,同樣也是使用 HTML 、CSS 和 JavaScript 進行開發。

作者

在學習 Svelte 之前先了解一下它的父親(作者)。

Svelte 的作者叫 Rich Harris[20] ,正在吃東西的這位就是他。

01.jpg

可能國內大多數工友對他不是很熟悉(我也完全不熟),但應該聽過 Rollup[21] 。

沒錯,他也是 Rollup[22] 的爸爸。

他在開發 Svelte 之前還開發過 Ractive.js[23] ,聽說 Vue 的部分實現也是受到了 Ractive 的啟發。

關於 Rich Harris 的介紹還有很多,我搜到的資料上這樣介紹到:

大學專業是學哲學的

在紐約時報調查組工作的圖形編輯,身兼記者和開發者職位

還有更多關於他和 Svelte 的介紹,可以看看 《Svelte \- The magical disappearing UI framework \- Interview with Rich Harris》[24]

Svelte 的優勢

Svelte 翻譯成中文就是“苗條”的意思,側面表明它打包出來的包非常小。

Svelte 主要優勢有以下幾點。

1). 編譯器

在開啟Svelte官網[25]時就能看到這樣的介紹。

Svelte 是一種全新的構建使用者介面的方法。傳統框架如 React 和 Vue 在瀏覽器中需要做大量的工作,而 Svelte 將這些工作放到構建應用程式的編譯階段來處理。

Svelte 元件需要在 .svelte 字尾的檔案中編寫,Svelte 會將編寫好的程式碼翻編譯 JS 和 CSS 程式碼。

2). 打包體積更小

Svelte 在打包會將引用到的程式碼打包起來,而沒引用過的程式碼將會被過濾掉,打包時不會加入進來。

在 《A RealWorld Comparison of Front-End Frameworks with Benchmarks \(2019 update\)》[26] 報告中,對主流框架進行了對比。

02.png

在經過 gzip 壓縮後生成的包大小,從報告中可以看出,Svelte 打包出來的體積甩開 Vue、React 和 Angular 幾條街。

這是因為經過 Svelte 編譯的程式碼,僅保留引用到的部分。

3). 不使用 Virtual DOM

Virtual DOM 就是 虛擬DOM,是用 JS 物件描述 DOM 節點的資料,由 React 團隊推廣出來的。

虛擬DOM 是前端的網紅,因此也有很多開發者開始研究和搞辯論賽。

網上有一張圖對比了 Svelte 和 React 在資料驅動檢視的流程

03.png

其實主要對比了使用虛擬DOM和直接操作真實DOM的區別。

在 React 中實現資料驅動檢視大概流程是這樣的:

資料發生變化 -> 通過diff演算法判斷要更新哪些節點 -> 找到要更新的節點 -> 更新真實DOM
複製程式碼

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》[27] 是 Svelte 官網上的一篇部落格,專門討論虛擬DOM。有興趣的工友可以看看~

4). 更自然的響應式

這也是我剛接觸 Svelte 時立刻喜歡上的理由。

這裡說的響應式設計是隻關於資料的響應,而不是像 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 會自動響應,而不需再做其他操作。

04.gif

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

05.gif

<h1>{name}</h1>
<script>
 let name = '雷猴'
 setTimeout(() => {
   name = '鯊魚辣椒'
 }, 1000)
</script>
複製程式碼

上面的程式碼中,1秒後修改 name 的值,並更新檢視。

從程式碼就能看出,在使用 Svelte 開發專案時,開發者一般無需使用額外的方法就能做到和 Vue、React 的響應式效果。

如果你對 Svelte 響應式原理感興趣,推薦閱讀 FESKY[28] 的 《Svelte 響應式原理剖析 —— 重新思考 Reactivity》[29]

也可以看看 《Rethinking reactivity》[30],看看官方對 reactivity 的思考。

5). 效能強

Stefan Krause 給出一份 效能測試報告(點選可檢視)[31] 對比裡多個熱門框架的效能。從 Svelte 的效能測試結果可以看出,Svelte 是相當優秀的。

6). 記憶體優化

效能測試報告(點選可檢視)[32] 也列出不同框架的記憶體佔用程度,Svelte 對記憶體的管理做到非常極致,佔用的記憶體也是非常小,這對於配置不高的裝置來說是件好事。

第5和6點,由於測試報告比較長,我沒截圖放進文中。大家有興趣可以點開連結檢視測試報告[33]。

7). 更關注無障礙體驗

在使用 Svelte 開發時會 自動對無障礙訪問方面的體驗進行檢測,比如 img 元素沒有新增 alt 屬性,Svelte 會向你發出一條警告。無障礙體驗對特殊人事來說是很有幫助的,比如當你在 img 標籤中設定好 alt 屬性值,使用有聲瀏覽器會把 alt 的內容讀出來。

在此我還要推薦2本關於設計體驗的書。

  • 《點石成金:訪客至上的Web和移動可用性設計祕笈》
  • 《包容性Web設計》

它們的封面長分別這個樣子

06.jpg

07.jpg

Svelte 的優勢肯定還有很多,但由於我開發經驗不足,只能總結出以上這些了。如果你對 Svelte 有更多理解,歡迎在評論區補充~

Svelte 的不足

  1. Svelte 對 IE 是非常不友好的,但我並不把這放在眼裡。如果想相容 IE 我還是推薦使用 jQuery。
  2. Svelte 的生態不夠豐富。由於是“新寵”,生態方面肯定是不如 Vue 和 React 的。

與 Svelte 相關的庫

Sapper

Sapper 官網地址[34]

Sapper 是構建在 Svelte 上的框架,Sapper 提供了頁面路由、佈局模板、SSR等功能。

Svelte Native

Svelte Native 官網地址[35]

Svelte Native 是建立在 NativeScript[36] 之上的產物,可以開發安卓和iOS應用,是一個跨端技術。

有點類似於 React Native 和 Weex 之類的東西。

svelte-gl

svelte-gl 倉庫[37]

svelte-gl 還沒正式釋出,但這是個很有趣的工具,它和 three.js[38] 類似,專門做 3D應用的。

雖然現在 github 上的 Star 還不是很多,但也可以寫些 demo 玩玩。

建立專案

在開始之前,你需要在電腦上安裝 Node[39] 環境。

編輯工具我使用了 VS Code ,同時安裝了 Svelte for VS Code 擴充套件外掛[40] 。

使用 Svelte 前,必須有一個開發環境。

建立或使用開發環境有以下幾種方式:

  1. REPL
  2. Rollup 版
  3. Webpack 版
  4. Parcel 版
  5. Vite 版

本文使用的是 Vite 建立專案,但上面列出的所有方式我都會逐一說說。

REPL

REPL 是 Svelte 提供的一個線上環境,開啟 Svelte 官網[41] 可以看到頂部導航欄上面有個 REPL[42] 的選項。點選該選項就可以跳轉到 Svelte 線上開發環境了。

08.png

09.png

REPL 是 read(讀取)、evaluate(執行)、print(列印) 和 loop(迴圈) 這幾個單詞的縮寫。

如果你只是想嘗試 Svelte 的某些功能或者測試小型程式碼,可以使用這款線上工具。

REPL 還提供了多元件開發,按左上角的 +號 可以建立新元件。元件的內容稍後會說到。

介面右側,頂部有3個選項:

  • Result: 執行結果。
  • JS output: Svelte 編譯後的 JS 程式碼。
  • CSS output: Svelte 編譯後的 CSS 程式碼。

10.png

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

11.png

當你在線上環境寫好程式碼,可以點選下載按鈕把專案儲存到本地,下載的檔案是一個 zip,需要自己手動解壓。

然後使用以下命令初始化專案並執行即可。

# 1、初始化專案
npm install
# 2、執行專案
npm run dev
# 3、在瀏覽器訪問 http://localhost:5000
複製程式碼

執行結果:

12.png

Rollup 版

Svelte 官方也提供了一個命令,可以下載 Svelte 專案到本地。

命令最後需要輸入你的專案名稱。

# 1、下載模板
npx degit sveltejs/template 專案名稱
# 2、安裝依賴
npm install
# 3、執行專案
npm run dev
# 4、在瀏覽器訪問 http://localhost:8080
複製程式碼

執行結果:

13.png

這是官方提供的建立專案方式,這個專案是使用 Rollup 打包的。

Rollup 和 Svelte 都是同一個作者(Rich Harris[43] )開發的,用回自家東西很正常。

Webpack 版

如果你不想使用 Rollup 打包專案,可以嘗試使用 Webpack。

# 1、下載模板
npx degit sveltejs/template-webpack 專案名稱
# 2、安裝依賴
npm install
# 3、執行專案
npm run dev
# 4、在瀏覽器訪問 http://localhost:8080/
複製程式碼

執行結果:

14.png

Parcel 版

我並 不推薦使用 該方法建立專案,因為 Svelte 並沒有提供使用 Parcel 打包工具的模板。但 GitHub 上有第三方的解決方案(點選訪問倉庫)[44]。

將 DeMoorJasper/parcel-plugin-svelte[45] 的程式碼下載下來。

# 1、進入 `packages/svelte-3-example` 目錄
# 2、安裝依賴
npm install
# 3、執行專案
npm run start
# 4、在瀏覽器訪問 http://localhost:1234/
複製程式碼

執行結果:

15.png

Vite 版

本文接下來所有例子都是使用 Vite 建立 Svelte 專案進行開發的。

使用 Vite 建立專案的原因是:快!

# 1、下載模板的命令
npm init vite@latest
# 2、輸入專案名
# 3、選擇 Svelte 模板(我沒選ts)
# 4、進入專案並安裝依賴
npm install
# 5、執行專案
npm run dev
# 6、在瀏覽器訪問 http://127.0.0.1:5173/
複製程式碼

執行結果:

16.png

本文使用 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 元素裡。

const app = new App({
 target: document.getElementById('app')
})
複製程式碼

target 指明目標元素。

我們大部分程式碼都是寫在 .svelte 字尾的檔案裡。

.svelte 檔案主要保安 多個 HTML 元素、1個 script 元素 和 1個 style 元素 。這3類元素都是可選的。

我們主要的工作目錄是 src 目錄。

為了減輕學習難度,我們先做這幾步操作。

1、清空全域性樣式

如果你使用 Rollup版 建立專案,不需要做這一步。

在使用 Vite 建立的 Svelte 專案中,找到 src/app.css 檔案,並把裡面的內容清空掉。

2、改造 src/App.svelte

將 src/App.svelte 檔案改成以下內容

<script>
 let name = '雷猴'
 function handleClick() {
   name = '鯊魚辣椒'
 }
</script>
<div>Hello {name}</div>
<button on:click={handleClick}>改名</button>
複製程式碼

此時點選按鈕,頁面上的 “雷猴” 就會變成 “鯊魚辣椒”

17.gif

上面的程式碼其實和 Vue 有點像。

  • 變數和方法都寫在 <script> 標籤裡。
  • 在 HTML 中使用 {} 可以繫結變數和方法。
  • 通過 on:click 可以繫結點選事件。

只需寫以上程式碼,Svelte 就會自動幫我們做資料響應的操作。一旦資料發生改變,檢視也會自動改變。

是不是非常簡單!

基礎模板語法

Svelte 的模板語法其實和 Vue 是有點像的。如果你之前已經使用過 Vue,那本節學起來就非常簡單。

插值

在 “起步章節” 已經使用過 插值 了。在 Svelte 中,使用 {} 大括號將 script 裡的資料繫結到 HTML 中。

18.png

<script>
 let name = '雷猴'
</script>
<div>{name}</div>
複製程式碼

此時頁面上就會出現 name 的值。

這種語法和 Vue 是有點像的,Vue 使用雙大括號的方式 {{}} 繫結資料。Svelte 就少一對括號。

表示式

在 HTML 中除了可以繫結變數外,還可以繫結表示式。

19.png

<script>
 let name = '雷猴'
 function sayHi() {
   return `${name} 世界!`
 }
 let a = 1
 let b = 2
 let state = false
</script>
<div>{sayHi()}</div>
<div>{a} + {b} = {a + b}</div>
<div>{state ? '雷猴' : '鯊魚辣椒'}</div>
複製程式碼

屬性繫結

HTML 的屬性需要動態繫結資料時,也是使用 {} 語法。

20.png

<script>
 let name = '雷猴'
</script>
<div title={name}>Hello</div>
複製程式碼

當滑鼠放到 div 標籤上時,會出現 title 裡的提示資訊。

渲染 HTML 標籤 @html

如果只是使用插值的方法渲染帶有 HTML 標籤的內容,Svelte 會自動轉義 < 、> 之類的標籤。

21.png

<script>
 let h1El = '<h1 style="color: pink;">雷猴</h1>'
</script>
<div>{h1El}</div>
複製程式碼

這種情況多數出現在渲染富文字。

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

22.png

<script>
 let h1El = '<h1 style="color: pink;">雷猴</h1>'
</script>
<div>{@html h1El}</div>
複製程式碼

但此方法有可能遭受 XSS 攻擊。

我在 《NodeJS 防止xss攻擊》[46] 中簡單演示過 XSS 攻擊,有興趣的可以看看。

樣式繫結

在日常開發中,給 HTML 標籤設定樣式主要通過 行內 style 和 class 屬性。

基礎的 HTML 寫法和原生的一樣,這裡不過多講解。

下面主要講動態設定樣式,也就是將 JS 裡的變數或者表示式繫結到 style 或者 class 裡。

行內樣式 style

23.gif

<script>
 let color = 'red'
 setTimeout(() => {
   color = 'blue'
 }, 1000)
</script>
<div style="color: {color}">雷猴</div>
複製程式碼

1秒後,文字從紅色變成藍色。

繫結 class

24.gif

<script>
 let foo = true
 setTimeout(() => {
   foo = false
 }, 1000)
</script>
<div class:active={foo}>雷猴</div>
<style>
 .active {
   color: red;
 }
</style>
複製程式碼

在 HTML 裡可以使用 class:xxx 動態設定要啟用的類。這裡的 xxx 是對應的類名。

語法是 class:xxx={state} ,當 state 為 true 時,這個樣式就會被啟用使用。

條件渲染 #if

使用 {#if} 開頭,{/if} 結尾。

基礎條件判斷

{#if 條件判斷}
...
{/if}
複製程式碼

舉個例子

25.gif

<script>
 let state = true
 setTimeout(() => {
   state = false
 }, 1000)
</script>
{#if state}
 <div>雷猴</div>
{/if}
複製程式碼

1秒後改變狀態

兩種條件

{#if 條件判斷}
...
{:else}
...
{/if}
複製程式碼

舉個例子

26.gif

<script>
 let state = true
 setTimeout(() => {
   state = false
 }, 1000)
</script>
{#if state}
 <div>雷猴</div>
{:else}
 <div>鯊魚辣椒</div>
{/if}
複製程式碼

多種條件

{#if 條件判斷}
...
{:else if 條件判斷}
...
{/if}
複製程式碼

舉個例子

27.gif

<script>
 let count = 1
 setInterval(() => {
   count++
 }, 1000)
</script>
{#if count === 1}
 <div>雷猴</div>
{:else if count === 2}
 <div>鯊魚辣椒</div>
{:else}
 <div>蟑螂惡霸</div>
{/if}
複製程式碼

條件渲染的用法比較簡單,只要 JS 基礎就能看得懂。

列表渲染 #each

如果你有一堆資料需要展示出來,可以使用 #each 方法。

使用 {#each} 開頭,{/each} 結尾。

遍歷陣列

{#each expression as name}
...
{/each}
複製程式碼
舉個例子

28.png

<script>
 let list = ['a', 'b', 'c', 'd', 'e', 'f']
</script>
<ul>
 {#each list as item}
  <li>{item}</li>
 {/each}
</ul>
複製程式碼

要注意,Svelte 和 Vue 的遍歷在寫法上有點不同。

Vue的方式是:

<div v-for="元素 in 源資料">
 <span>{{元素}}</span>
</div>
複製程式碼

Svelte的方式是:

<div>
 {#each 源資料 as 元素}
   <span>{元素}</span>
 {/each}
</div>
複製程式碼

遍歷陣列(帶下標)

29.png

<script>
 let list = ['a', 'b', 'c', 'd', 'e', 'f']
</script>
<ul>
 {#each list as item, index}
  <li>{index} -- {item}</li>
 {/each}
</ul>
複製程式碼

注意:as 後面首先跟著元素,然後才是下標。而且元素和下標不需要用括號括起來。

如果元素是物件,可以解構

30.png

<script>
 let list = [
   {name: '雷猴'},
   {name: '鯊魚辣椒'}
 ]
</script>
<ul>
 {#each list as {name}}
  <li>{name}</li>
 {/each}
</ul>
複製程式碼

預設內容

如果源資料沒有內容,是空陣列的情況下,還可以組合 {:else} 一起使用。

31.png

<script>
 let list = []
</script>
<div>
 {#each list as {name}}
  <div>{name}</div>
 {:else}
  <div>暫無資料</div>
 {/each}
</div>
複製程式碼

事件繫結 on:event

使用 on: 指令監聽 DOM 事件,on: 後面跟隨事件型別

語法:

on:事件型別={事件名}
複製程式碼

舉個例子,點選按鈕時在控制檯輸出 “雷猴”。

32.gif

<script>
 function sayHi() {
   console.log('雷猴')
 }
</script>
<button on:click={sayHi}>打招呼</button>
複製程式碼

繫結其他事件(比如change等)也是同樣的道理。

事件修飾符

如果你只希望某些事件只執行一次,或者取消預設行為,或者阻止冒泡等,可以使用事件修飾符。

語法:

on:事件型別|修飾符={事件名}
複製程式碼

舉個例子,我希望點選事件只能執行一次,之後再點選都無效,可以使用官方提供的 once 修飾符。

33.gif

<script>
 function sayHi() {
   console.log('雷猴')
 }
</script>
<button on:click|once={sayHi}>打招呼</button>
複製程式碼

從上圖可以看出,多次點選都只是輸出1次“雷猴”。

除了 once 之外,還有以下這些修飾符可以用:

  • preventDefault :禁止預設事件。在程式執行之前呼叫 event.preventDefault()
  • stopPropagation :呼叫 event.stopPropagation(), 防止事件到達下一個標籤
  • passive :改善了 touch/wheel 事件的滾動表現(Svelte會在合適的地方自動加上它)
  • capture:表示在 _capture_階段而不是_bubbling_觸發其程式
  • once :程式執行一次後刪除自身

串聯修飾符

修飾符還可以串聯起來使用,比如 on:click|once|capture={...}

但需要注意,有些特殊的標籤使用修飾符會出現“意想不到”的結果,比如 <a> 標籤。

34.gif

本來是想給 <a> 標籤繫結一個點選事件,第一次點選時在控制檯輸出一句話,並且禁止 <a> 標籤的預設事件。

所以使用了 once 和 preventDefault 修飾符。

但實際上並非如此。上面的程式碼意思是 once 設定了只執行一次 toLearn 事件,並且只有一次 preventDefault 是有效的。

只有點選時就不觸發 toLearn 了,而且 preventDefault 也會失效。所以再次點選時,<a> 元素就會觸發自身的跳轉功能。

資料繫結 bind

資料繫結通常會和表單元素結合使用。

bind 可以做到雙向資料繫結的效果。我覺得 Svelte 裡的 bind 有點像 Vue 的 v-model。

語法:

bind:property={variable}
複製程式碼

input 單行輸入框

35.gif

<script>
 let msg = 'hello'
 function print() {
   console.log(msg)
 }
</script>
<input type="text" value={msg} />
<button on:click={print}>列印</button>
複製程式碼

如果只是使用 value={msg} 的寫法,input 預設值是 hello ,當輸入框的值發生改變時,並沒有把內容反應回 msg 變數裡。

此時就需要使用 bind 了。

36.gif

<!-- 省略部分程式碼 -->
<input type="text" bind:value={msg} />
複製程式碼

textarea 多行文字框

多行文字框同樣繫結在 value 屬性上。

37.gif

<script>
 let msg = 'hello'
</script>
<textarea type="text" bind:value={msg} />
<p>{msg}</p>
複製程式碼

input range 範圍選擇

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

38.gif

<script>
 let val = 3
</script>
<input type="range" bind:value={val} min=0 max=10 />
<p>{val}</p>
複製程式碼

radio 單選

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

39.gif

<script>
 let selected = '2'
</script>
<input type="radio" bind:group={selected} value="1" />
<input type="radio" bind:group={selected} value="2" />
<input type="radio" bind:group={selected} value="3" />
<p>{selected}</p>
複製程式碼

checkbox 複選框

40.gif

<script>
 let roles = []
</script>
<input type="checkbox" bind:group={roles} value="雷猴" />
<input type="checkbox" bind:group={roles} value="鯊魚辣椒" />
<input type="checkbox" bind:group={roles} value="蟑螂惡霸" />
<input type="checkbox" bind:group={roles} value="蠍子萊萊" />
<p>{roles}</p>
複製程式碼

select 選擇器

41.gif

<script>
 let selected = 'a'
</script>
<select bind:value={selected}>
<option value='a'>a</option>
<option value='b'>b</option>
<option value='c'>c</option>
</select>
<span>{selected}</span>
複製程式碼

select multiple 選擇器

multiple 和 checkbox 有點像。

42.gif

<script>
 let selected = []
</script>
<select multiple bind:value={selected}>
<option value="雷猴">雷猴</option>
<option value="鯊魚辣椒">鯊魚辣椒</option>
<option value="蟑螂惡霸">蟑螂惡霸</option>
<option value="蠍子萊萊">蠍子萊萊</option>
</select>
<span>{selected}</span>
複製程式碼

簡寫形式

如果 bind 繫結的屬性和在 JS 裡宣告的變數名相同,那可以直接繫結

43.gif

<script>
 let value = 'hello'
</script>
<input type="text" bind:value />
<p>{value}</p>
複製程式碼

這個例子中,bind:value 繫結的屬性是 value ,而在 JS 中宣告的變數名也叫 value ,此時就可以使用簡寫的方式。

$: 宣告反應性

  • 通過使用$: JS label 語法[47]作為字首。可以讓任何位於 top-level 的語句(即不在塊或函式內部)具有反應性。每當它們依賴的值發生更改時,它們都會在 component 更新之前立即執行。

上面這段解釋是官方文件的解釋。

$: 在文件中稱為 Reactivity ,中文文件成它為 反應效能力。

但我使用 $: 時,覺得這個功能有點像 Vue 的 computed。

$: 可以監聽表示式內部的變化從而做出響應。

44.gif

<script>
 let count = 0;
 $: doubled = count * 2;
 function handleClick() {
   count += 1;
 }
</script>
<button on:click={handleClick}>
 點選加1
</button>
<p>{count} 翻倍後 {doubled}</p>
複製程式碼

使用 $: 宣告的 double 會自動根據 count 的值改變而改變。

如果將以上程式碼中 $: 改成 let 或者 var 宣告 count ,那麼 count 將失去響應性。

這樣看來,真的和 Vue 的 computed 的作用有那麼一點像。

非同步渲染 #await

Svelte 提供非同步渲染標籤,可以提升使用者體驗。

語法:

{#await expression}
...
{:then name}
...
{:catch name}
...
{/await}
複製程式碼

以 #await 開始,以 /await 結束。

:then 代表成功結果,:catch 代表失敗結果。

expression 是判斷體,要求返回一個 Promise。

其實用法和 #if ... :else if ... /if 有那麼一丟丟像。

舉個例子

45.gif

<script>
 const api = new Promise((resolve, reject) => {
   setTimeout(() => {
     resolve('請求成功,資料是xxxxx')
   }, 1000)
 })
</script>
{#await api}
 <span>Loading...</span>
{:then response}
 <span>{response}</span>
{:catch error}
 <span>{error}</span>
{/await}
複製程式碼

如果將上面的 resolve 改成 reject 就會走 :catch 分支。

基礎元件

在 Svelte 中,建立元件只需要建立一個 .svelte 為字尾的檔案即可。

通過 import 引入子元件。

比如,在 src 目錄下有 App.svelte 和 Phone.svelte 兩個元件。

App.svelte 是父級,想要引入 Phone.svelte 並在 HTML 中使用。

46.png

App.svelte

<script>
 import Phone from './Phone.svelte'
</script>
<div>子元件 Phone 的內容:</div>
<Phone />
複製程式碼

Phone.svelte

<div>電話:13266668888</div>
複製程式碼

​元件通訊

元件通訊主要是 父子元件 之間的資料來往。

父傳子

比如上面的例子,手機號希望從 App.svelte 元件往 Phone.svelte 裡傳。

可以在 Phone.svelte 中宣告一個變數,並公開該變數。

App.svelte 就可以使用對應的屬性把值傳入。

47.png

App.svelte

<script>
 import Phone from './Phone.svelte'
</script>
<div>子元件 Phone 的內容:</div>
<Phone number="88888888" />
複製程式碼

Phone.svelte

<script>
 export let number = '13266668888'
</script>
<div>電話:{number}</div>
複製程式碼

如果此時 App.svelte 元件沒有傳值進來,Phone.svelte 就會使用預設值。

子傳父

如果想在子元件中修改父元件的內容,需要把修改的方法定義在父元件中,並把該方法傳給子元件呼叫。

同時需要在子元件中引入 createEventDispatcher 方法。

48.gif

App.svelte

<script>
 import Phone from './Phone.svelte'
 function print(data) {
   console.log(`手機號: ${data.detail}`)
 }
</script>
<div>子元件 Phone 的內容:</div>
<Phone on:printPhone={print} />
複製程式碼

Phone.svelte

<script>
 import { createEventDispatcher } from 'svelte'
 const dispatch = createEventDispatcher()
 function printPhone() {
   dispatch('printPhone', '13288888888')
 }
</script>
<button on:click={printPhone}>輸出手機號</button>
複製程式碼

父元件接受引數是一個物件,子元件傳過來的值都會放在 detail 屬性裡。

插槽 slot

和 Vue 一樣,Svelte 也有元件插槽。

在子元件中使用 <slot> 標籤,可以接收父元件傳進來的 HTML 內容。

49.png

App.svelte

<script>
 import Phone from './Phone.svelte'
</script>
<div>子元件 Phone 的內容:</div>
<Phone>
 <div>電話:</div>
 <div>13288889999</div>
</Phone>
複製程式碼

Phone.svelte

<style>
.box {
 width: 100px;
 border: 1px solid #aaa;
 border-radius: 8px;
 box-shadow: 2px 2px 8px rgba(0,0,0,0.1);
 padding: 1em;
 margin: 1em 0;
}
</style>
<div class="box">
<slot>預設值</slot>
</div>
複製程式碼

生命週期

生命週期是指專案執行時,指定時期會自動執行的方法。

Svelte 中主要有以下幾個生命週期:

  • onMount: 元件掛載時呼叫。
  • onDestroy: 元件銷燬時執行。
  • beforeUpdate: 在資料更新前執行。
  • afterUpdate: 在資料更新完成後執行。
  • tick: DOM元素更新完成後執行。

以上生命週期都是需要從 svelte 裡引入的。

用 onMount 舉個例子

50.gif

<script>
 import { onMount } from 'svelte'
 let title = 'Hello world'

 onMount(() => {
   console.log('onMount')
   setTimeout(() => title = '雷猴', 1000)
 })
</script>
<h1>{title}</h1>
複製程式碼

在元件載入完1秒後,改變 title 的值。

onDestroy、beforeUpdate 和 afterUpdate 都和 onMount 的用法差不多,只是執行的時間條件不同。你可以自己建立個專案試試看。

tick 是比較特殊的,tick 和 Vue 的 nextTick 差不多。

在 Svelte 中,tick 的使用語法如下:

import { tick } from 'svelte'
await tick()
// 其他操作
複製程式碼

總結

本文主要講解了 Svelte 的基礎用法,但 Svelte 的內容和 API 遠不止此。它還有很多高階的用法以及提供了過渡動畫功能等,這些都會放在高階篇講解。

Svelte 是一個 Web 應用的構建工具,它打包出來的專案體積比較小,效能強,不使用虛擬DOM。

但 Svelte 的相容性和周邊生態相比起 Vue 和 React 會差一點。

所以日常專案中需要根據 Svelte 的優缺點進行取捨。