BACK
Featured image of post 前端新寵兒? - Svelte 介紹

前端新寵兒? - Svelte 介紹

Svelte 應用和組件由 .svelte 文件定義。這是一種添加了類似於JSX的模板語法的HTML文件。Svelte 使用 $: 語法來標記響應式的組件。頂層的變量是組件的狀態,導出的變量成為組件接收的屬性。本文適合人群:有HTML、CSS、JS基礎,知道並已經安裝了Node。

參考網站
參考網站
參考網站
參考網站

其實在很久之前我就注意到 Svelte,但一直沒把這個框架放在心上。
因為我之前的工作主要使用 Vue,但完全沒遇到過使用 Svelte 的項。
直到 Vite 的出現,我才開始開始重視 Svelte

Vite 文檔 裡可以看到它支持這些模板:

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

我喜歡用 Demo 的方式學習新技術,Svelte 官方入門教程 就提供了這種方式。
這是我覺得入門比較舒服且方便日後搜索的學習方式。
雖然 Svelte 官方入門教程 已經給出很多例子,而且 Svelte中文網 也有對應的翻譯,但有些翻譯看上去是機譯,而且部分案例可能不太適合新手學習~

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

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

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


Svelte 簡介

Svelte 是一個構建web 應用程序的工具。

傳統框架如React 和Vue 在瀏覽器中需要做大量的工作,而Svelte 將這些工作放到構建應用程序的編譯階段來處理。

需要注意,Svelte 是一款編譯器。它可以將按照規定語法編寫的代碼打包成瀏覽器能運行的項目。
和其他前端框架一樣,同樣也是使用HTMLCSSJavaScript 進行開發。

作者

在學習 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 主要優勢有以下幾點。

  1. 編譯器

在打開 Svelte官網 時就能看到這樣的介紹。
Svelte 是一種全新的構建用戶界面的方法。傳統框架如 React 和 Vue 在瀏覽器中需要做大量的工作,而 Svelte 將這些工作放到構建應用程序的編譯階段來處理。
Svelte 組件需要在 .svelte 後綴的文件中編寫,Svelte 會將編寫好的代碼翻編譯 JSCSS 代碼。

  1. 打包體積更小

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

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

在經過 gzip 壓縮後生成的包大小,從報告中可以看出,Svelte 打包出來的體積甩開 Vue、React 和 Angular 幾條街。
這是因為經過 Svelte 編譯的代碼,僅保留引用到的部分。

  1. 不使用 Virtual DOM

Virtual DOM 就是 虛擬DOM,是用 JS 對象描述 DOM 節點的數據,由 React 團隊推廣出來的。
虛擬DOM 是前端的網紅,因此也有很多開發者開始研究和搞辯論賽。
網上有一張圖對比了 Svelte 和 React 在數據驅動視圖的流程

其實主要對比了使用虛擬DOM和直接操作真實DOM的區別。 在 React 中實現數據驅動視圖大概流程是這樣的:

1
數據發生變化 -> 通過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》是 Svelte 官網上的一篇博客,專門討論虛擬DOM。有興趣的工友可以看看~

  1. 更自然的響應式

這裡說的響應式設計是只關於數據的響應,而不是像 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
2
3
4
5
6
7
8
9
<h1>{name}</h1>

<script>
  let name = '雷猴'

  setTimeout(() => {
    name = '鯊魚辣椒'
  }, 1000)
</script>

上面的代碼中,1秒後修改 name 的值,並更新視圖。
從代碼就能看出,在使用 Svelte 開發項目時,開發者一般無需使用額外的方法就能做到和 Vue、React 的響應式效果。

如果你對 Svelte 響應式原理感興趣,推薦閱讀 FESKY《Svelte 響應式原理剖析—— 重新思考Reactivity》
也可以看看 《Rethinking reactivity》,看看官方對 reactivity 的思考。

  1. 性能強

Stefan Krause 給出一份 性能測試報告(點擊可查看) 對比里多個熱門框架的性能。從 Svelte 的性能測試結果可以看出,Svelte 是相當優秀的。

  1. 內存優化

性能測試報告(點擊可查看) 也列出不同框架的內存佔用程度,Svelte 對內存的管理做到非常極致,佔用的內存也是非常小,這對於配置不高的設備來說是件好事。

第5、6點,由於測試報告比較長,我沒截圖放進文中。大家有興趣可以點開鏈接查看測試報告

  1. 更關注無障礙體驗

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

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

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

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

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

Svelte 的不足

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

與 Svelte 相關的庫

Sapper

Sapper 官網地址

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

Svelte Native

Svelte Native 官網地址

Svelte Native 是建立在 NativeScript 之上的產物,可以開發安卓和iOS應用,是一個跨端技術。
有點類似於 React Native 和 Weex 之類的東西。

svelte-gl

svelte-gl Github

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 線上開發環境了。

REPLread(讀取)evaluate(執行)print(打印)loop(循環) 這幾個單詞的縮寫。

如果你只是想嘗試 Svelte 的某些功能或者測試小型代碼,可以使用這款線上工具。
REPL還提供了多組件開發,按左上角的 可以創建新組件。組件的內容稍後會說到。

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

  • Result: 運行結果。
  • JS output: Svelte 編譯後的 JS 代碼。
  • CSS output: Svelte 編譯後的 CSS 代碼。

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

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

1
2
3
4
5
6
7
# 1. 初始化項目
npm install

# 2. 運行項目
npm run dev

# 3. 在瀏覽器訪問 http://localhost:5000

運行結果:

Rollup 版

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 1. 下載模板
npx degit sveltejs/template 項目名稱

# 2. 安裝依賴
npm install

# 3. 運行項目
npm run dev

# 4. 在瀏覽器訪問 http://localhost:8080

運行結果:

這是官方提供的創建項目方式,這個項目是使用 Rollup 打包的。 Rollup 和 Svelte 都是同一個作者(Rich Harris)開發的,用回自家東西很正常。

Webpack 版

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 1. 下載模板
npx degit sveltejs/template-webpack 項目名稱

# 2. 安裝依賴
npm install

# 3. 運行項目
npm run dev

# 4. 在瀏覽器訪問 http://localhost:8080

運行結果:

Parcel 版

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

DeMoorJasper/parcel-plugin-svelte的代碼下載下來。

1
2
3
4
5
6
7
8
9
# 1. 進入 `packages/svelte-3-example` 目錄

# 2. 安裝依賴
npm install

# 3. 運行項目
npm run start

# 4. 在瀏覽器訪問 http://localhost:1234

運行結果:

Vite 版

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 1. 下載模板
npm init vite@latest

# 2. 輸入項目名稱

# 3. 選擇 Svelte 模板(我沒選ts)

# 4. 安裝依賴
npm install

# 5. 運行項目
npm run dev

# 6. 在瀏覽器訪問 http://127.0.0.1:5173

運行結果:

本文使用 Vite 創建項目,目錄結構和Rollup版創建出來的項目結構稍微有點不同,但開發邏輯是一樣的。


起步

index.htmlsrc/main.jssrc/App.svelte 這三個是最主要的文件。
index.html 是項目運行的入口文件,它裡面引用了 src/main.js 文件。
src/main.js 裡引入了 src/App.svelte 組件,並使用以下代碼將 src/App.svelte 的內容渲染到 #app 元素裡。

1
2
3
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 文件,並把裡面的內容清空掉。

  1. 改造 src/App.svelte

src/App.svelte 文件改成以下內容

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<script>
  let name = '雷侯'

  function handleClick() {
    name = '鯊魚辣椒'
  }
</script>

<div>Hello {name}</div>
<button on:click={handleClick}>改名</button>

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

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

  • 變量和方法都寫在 <script> 標籤裡。
  • HTML 中使用 {} 可以綁定變量和方法。
  • 通過 on:click 可以綁定點擊事件。

只需寫以上代碼,Svelte 就會自動幫我們做數據響應的操作。一旦數據發生改變,視圖也會自動改變。
是不是非常簡單!


基礎模板語法

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

插值

在"起步章節" 已經使用過 插值 了。在 Svelte 中,使用 {} 大括號將 script 裡的數據綁定到 HTML 中。

1
2
3
4
5
<script>
  let name = '雷猴'
</script>

<div>{name}</div>

此時頁面上就會出現 name 的值。
這種語法和 Vue 是有點像的,Vue 使用雙大括號的方式 {{}} 綁定數據。 Svelte 就少一對括號。

表達式

HTML 中除了可以綁定變量外,還可以綁定表達式。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<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 的屬性需要動態綁定數據時,也是使用 {} 語法。

1
2
3
4
5
<script>
  let name = '雷猴'
</script>

<div title={name}>Hello</div>

當鼠標放到 div 標籤上時,會出現 title 裡的提示信息。

渲染 HTML 標籤 @html

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

1
2
3
4
5
<script>
  let h1El = '<h1 style="color: pink;">雷猴</h1>'
</script>

<div>{h1El}</div>

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

1
2
3
4
5
<script>
  let h1El = '<h1 style="color: pink;">雷猴</h1>'
</script>

<div>{@html h1El}</div>

但此方法有可能遭受 XSS 攻擊。
我在 「NodeJS 防止 xss 攻擊」 中簡單演示過 XSS 攻擊,有興趣的可以看看。


樣式綁定

在日常開發中,給 HTML 標籤設置樣式主要通過 行内 styleclass 屬性。
基礎的 HTML 寫法和原生的一樣,這裡不過多講解。
下面主要講動態設置樣式,也就是將 JS 裡的變量或者表達式綁定到 style 或者 class 裡。

行內樣式 style

1
2
3
4
5
6
7
8
9
<script>
  let color = 'red'

  setTimeout(() => {
    color = 'blue'
  }, 1000)
</script>

<div style="color: {color}">雷猴</div>

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

綁定class

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<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},當 statetrue 時,這個樣式就會被激活使用。


條件渲染 #if

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

基礎條件判斷

1
2
3
{#if 条件判断}
...
{/if}

舉個例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<script>
  let state = true

  setTimeout(() => {
    state = false
  }, 1000)
</script>

{#if state}
  <div>雷猴</div>
{/if}

1秒後改變狀態

兩種條件

1
2
3
4
5
{#if 条件判断}
...
{:else}
...
{/if}

舉個例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<script>
  let state = true

  setTimeout(() => {
    state = false
  }, 1000)
</script>

{#if state}
  <div>雷猴</div>
{:else}
  <div>鯊魚辣椒</div>
{/if}

多種條件

1
2
3
4
5
{#if 條件判斷}
...
{:else if 條件判斷}
...
{/if}

舉個例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<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} 結尾。

遍歷數組

1
2
3
{#each expression as name}
...
{/each}

舉個例子

1
2
3
4
5
6
7
8
9
<script>
  let list = ['a', 'b', 'c', 'd', 'e', 'f']
</script>

<ul>
  {#each list as item}
  	<li>{item}</li>
  {/each}
</ul>

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

Vue 的方式是:

1
2
3
<div v-for="元素 in 源數據">
  <span>{{元素}}</span>
</div>

Svelte 的方式是:

1
2
3
4
5
<div>
  {#each 源數據 as 元素}
    <span>{元素}</span>
  {/each}
</div>

遍歷數組(帶 index)

1
2
3
4
5
6
7
8
9
<script>
  let list = ['a', 'b', 'c', 'd', 'e', 'f']
</script>

<ul>
  {#each list as item, index}
  	<li>{index} -- {item}</li>
  {/each}
</ul>

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

如果元素是對象,可以解構

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<script>
  let list = [
    {name: '雷猴'},
    {name: '鯊魚辣椒'}
  ]
</script>

<ul>
  {#each list as {name}}
  	<li>{name}</li>
  {/each}
</ul>

默認內容

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<script>
  let list = []
</script>

<div>
  {#each list as {name}}
  	<div>{name}</div>
  {:else}
  	<div>暫無數據</div>
  {/each}
</div>

事件綁定 on:event

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

1
on:事件類型={事件名}

舉個例子,點擊按鈕時在控制台輸出"雷猴"。

1
2
3
4
5
6
7
<script>
  function sayHi() {
    console.log('雷猴')
  }
</script>

<button on:click={sayHi}>打招呼</button>

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


事件修飾符

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

1
on:事件類型|修飾符={事件名}

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

1
2
3
4
5
6
7
<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> 標籤。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<script>
  function toLearn() {
    console.log('還在思考要不要學Canvas')
  }
</script>

<a
  href="https://juejin.cn/post/7116784455561248775"
  on:click|once|preventDefault={toLearn}
>去學習Canvas ?</a>

本來是想給 <a> 標籤綁定一個點擊事件,第一次點擊時在控制台輸出一句話,並且禁止 <a> 標籤的默認事件。
所以使用了 oncepreventDefault 修飾符。
但實際上並非如此。上面的代碼意思是 once 設定了只執行一次 toLearn 事件,並且只有一次 preventDefault 是有效的。
只有點擊時就不觸發 toLearn 了,而且 preventDefault 也會失效。所以再次點擊時,<a>元素就會觸發自身的跳轉功能。


數據綁定 bind

數據綁定通常會和表單元素結合使用。
bind 可以做到雙向數據綁定的效果。我覺得 Svelte 裡的 bind 有點像 Vue 的 v-model

語法:

1
bind:property={variable}

input 單行輸入框

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<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 了。

1
2
<!-- 省略部分代碼 -->
<input type="text" bind:value={msg} />

textarea 多行文本框

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

1
2
3
4
5
6
<script>
  let msg = 'hello'
</script>

<textarea type="text" bind:value={msg} />
<p>{msg}</p>

input range 範圍選擇

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

1
2
3
4
5
6
<script>
  let val = 3
</script>

<input type="range" bind:value={val} min=0 max=10 />
<p>{val}</p>

radio 單選

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

1
2
3
4
5
6
7
8
<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 複選框

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<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 選擇器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<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 選擇器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<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 裡聲明的變量名相同,那可以直接綁定

1
2
3
4
5
6
7
<script>
  let value = 'hello'
</script>

<input type="text" bind:value />

<p>{value}</p>

這個例子中, bind:value 綁定的屬性是 value,而在 JS 中聲明的變量名也叫 value,此時就可以使用簡寫的方式。


$: 聲明反應性

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

上面這段解釋是官方文檔的解釋。
$:在文檔中稱為Reactivity,中文文檔成它為反應性能力
但我使用 $: 時,覺得這個功能有點像 Vue 的 computed
$: 可以監聽表達式內部的變化從而做出響應。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<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 提供異步渲染標籤,可以提升用戶體驗。
語法:

1
2
3
4
5
6
7
{#await expression}
...
{:then name}
...
{:catch name}
...
{/await}

#await 開始、以 /await 結束。
:then 代表成功結果,:catch 代表失敗結果。
expression是判斷體,要求返回一個Promise
其實用法和 #if ... :else if ... /if 有那麼一丟丟像。

舉個例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<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.sveltePhone.svelte 兩個組件。
App.svelte 是父級,想要引入 Phone.svelte 並在 HTML 中使用。

App.svelte
1
2
3
4
5
6
<script>
  import Phone from './Phone.svelte'
</script>

<div>子組件 Phone 的内容:</div>
<Phone />
Phone.svelte
1
<div>電話:13266668888</div>

組件通訊

組件通訊主要是 父子組件 之間的數據來往。

父傳子

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

App.svelte
1
2
3
4
5
6
<script>
  import Phone from './Phone.svelte'
</script>

<div>子組件 Phone 的内容:</div>
<Phone number="88888888" />
Phone.svelte
1
2
3
4
5
<script>
  export let number = '13266668888'
</script>

<div>電話:{number}</div>

如果此時 App.svelte 組件沒有傳值進來,Phone.svelte 就會使用默認值。

子傳父

如果想在子組件中修改父組件的內容,需要把修改的方法定義在父組件中,並把該方法傳給子組件調用。
同時需要在子組件中引入 createEventDispatcher 方法。

App.svelte
1
2
3
4
5
6
7
8
9
<script>
  import Phone from './Phone.svelte'
  function print(data) {
    console.log(`手機號碼:${data.detail}`)
  }
</script>

<div>子組件 Phone 的内容:</div>
<Phone on:printPhone={print} />
Phone.svelte
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<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 內容。

App.svelte
1
2
3
4
5
6
7
8
9
<script>
  import Phone from './Phone.svelte'
</script>

<div>子組件 Phone 的内容:</div>
<Phone>
  <div>電話:</div>
  <div>13288889999</div>
</Phone>
Phone.svelte
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<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 舉個例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<script>
  import { onMount } from 'svelte'
  let title = 'Hello world'
  
  onMount(() => {
    console.log('onMount')
    setTimeout(() => title = '雷猴', 1000)
  })
</script>

<h1>{title}</h1>

在組件加載完1秒後,改變 title 的值。

onDestroybeforeUpdateafterUpdate 都和 onMount 的用法差不多,只是執行的時間條件不同。

tick 是比較特殊的,tick 和 Vue 的 nextTick 差不多。
在 Svelte 中,tick 的使用語法如下:

1
2
3
4
5
<script>
import { tick } from 'svelte'
await tick()
// 其他操作
</script>

總結

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

Svelte 是一個 Web 應用的構建工具,它打包出來的項目體積比較小,性能強,不使用虛擬DOM。
Svelte 的兼容性和周邊生態相比起 VueReact 會差一點。
所以日常項目中需要根據 Svelte 的優缺點進行取捨。


Licensed under CC BY-NC-SA 4.0
comments powered by Disqus