Svelte[1] 是我用過最爽的框架,就算 Vue 和 React 再強大,生態再好,我還是更喜歡 Svelte,因為它開發起來真的很爽。
其實在很久之前我就注意到 Svelte[2] ,但一直沒把這個框架放在心上。
因為我之前的工作主要使用 Vue,偶爾也會接觸到一些 React 項目,但完全沒遇到過使用 Svelte 的項。
直到 Vite 的出現,我才開始開始重視 Svelte。
從 Vite 文檔[3] 里可以看到它支持這些模板:
能讓祖師爺也重視的框架,不簡單不簡單~
我喜歡用 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] ,正在吃東西的這位就是他。

可能國內大多數工友對他不是很熟悉(我也完全不熟),但應該聽過 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] 報告中,對主流框架進行了對比。

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

其實主要對比了使用虛擬 DOM 和直接操作真實 DOM 的區別。
在 React 中實現數據驅動視圖大概流程是這樣的:
數據發生變化->通過diff算法判斷要更新哪些節點->找到要更新的節點->更新真實DOMVue 的數據更新原理其實也差不多,只是實現方式和使用語法會有所不同。
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 會自動響應,而不需再做其他操作。

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

上面的代碼中,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 本關於設計體驗的書。
它們的封面長分別這個樣子


Svelte 的優勢肯定還有很多,但由於我開發經驗不足,只能總結出以上這些了。如果你對 Svelte 有更多理解,歡迎在評論區補充~
Svelte 的不足Sapper 官網地址[34]
Sapper 是構建在 Svelte 上的框架,Sapper 提供了頁面路由、布局模板、SSR 等功能。
Svelte NativeSvelte Native 官網地址[35]
Svelte Native 是建立在 NativeScript[36] 之上的產物,可以開發安卓和 iOS 應用,是一個跨端技術。
有點類似於 React Native 和 Weex 之類的東西。
svelte-glsvelte-gl 倉庫[37]
svelte-gl 還沒正式發布,但這是個很有趣的工具,它和 three.js[38] 類似,專門做 3D 應用的。
雖然現在 github 上的 Star 還不是很多,但也可以寫些 demo 玩玩。
創建項目在開始之前,你需要在電腦上安裝 Node[39] 環境。
編輯工具我使用了 VS Code ,同時安裝了 Svelte for VS Code 擴展插件[40] 。
使用 Svelte 前,必須有一個開發環境。
創建或使用開發環境有以下幾種方式:
本文使用的是 Vite 創建項目,但上面列出的所有方式我都會逐一說說。
REPLREPL 是 Svelte 提供的一個線上環境,打開 Svelte 官網[41] 可以看到頂部導航欄上面有個 REPL[42] 的選項。點擊該選項就可以跳轉到 Svelte 線上開發環境了。


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

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

當你在線上環境寫好代碼,可以點擊下載按鈕把項目保存到本地,下載的文件是一個 zip,需要自己手動解壓。
然後使用以下命令初始化項目並運行即可。
#1、初始化項目npminstall#2、運行項目npmrundev#3、在瀏覽器訪問http://localhost:5000運行結果:

Svelte 官方也提供了一個命令,可以下載 Svelte 項目到本地。
命令最後需要輸入你的項目名稱。
#1、下載模板npxdegitsveltejs/template項目名稱#2、安裝依賴npminstall#3、運行項目npmrundev#4、在瀏覽器訪問http://localhost:8080運行結果:

這是官方提供的創建項目方式,這個項目是使用 Rollup 打包的。
Rollup 和 Svelte 都是同一個作者(Rich Harris[43] )開發的,用回自家東西很正常。
Webpack 版如果你不想使用 Rollup 打包項目,可以嘗試使用 Webpack。
#1、下載模板npxdegitsveltejs/template-webpack項目名稱#2、安裝依賴npminstall#3、運行項目npmrundev#4、在瀏覽器訪問http://localhost:8080/運行結果:

我並 不推薦使用 該方法創建項目,因為 Svelte 並沒有提供使用 Parcel 打包工具的模板。但 GitHub 上有第三方的解決方案(點擊訪問倉庫)[44]。
將 DeMoorJasper/parcel-plugin-svelte[45] 的代碼下載下來。
#1、進入`packages/svelte-3-example`目錄#2、安裝依賴npminstall#3、運行項目npmrunstart#4、在瀏覽器訪問http://localhost:1234/運行結果:

本文接下來所有例子都是使用 Vite 創建 Svelte 項目進行開發的。
使用 Vite 創建項目的原因是:快!
#1、下載模板的命令npminitvite@latest#2、輸入項目名#3、選擇Svelte模板(我沒選ts)#4、進入項目並安裝依賴npminstall#5、運行項目npmrundev#6、在瀏覽器訪問http://127.0.0.1:5173/運行結果:

本文使用 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 元素里。
constapp=newApp({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>letname='雷猴'functionhandleClick(){name='鯊魚辣椒'}</script><div>Hello{name}</div><buttonon:click={handleClick}>改名</button>此時點擊按鈕,頁面上的 「雷猴」 就會變成 「鯊魚辣椒」

上面的代碼其實和 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 攻擊》[46] 中簡單演示過 XSS 攻擊,有興趣的可以看看。
樣式綁定在日常開發中,給 HTML 標籤設置樣式主要通過 行內 style 和 class 屬性。
基礎的 HTML 寫法和原生的一樣,這裡不過多講解。
下面主要講動態設置樣式,也就是將 JS 里的變量或者表達式綁定到 style 或者 class 里。
行內樣式 style
1 秒後,文字從紅色變成藍色。
綁定 class
在 HTML 里可以使用 class:xxx 動態設置要激活的類。這裡的 xxx 是對應的類名。
語法是 class:xxx={state} ,當 state 為 true 時,這個樣式就會被激活使用。
條件渲染 #if使用 {#if} 開頭,{/if} 結尾。
基礎條件判斷{#if條件判斷}...{/if}舉個例子

1 秒後改變狀態
兩種條件{#if條件判斷}...{:else}...{/if}舉個例子

舉個例子

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

要注意,Svelte 和 Vue 的遍歷在寫法上有點不同。
Vue 的方式是:
<divv-for="元素in源數據"><span>{{元素}}</span></div>Svelte 的方式是:
<div>{#each源數據as元素}<span>{元素}</span>{/each}</div>遍歷數組(帶下標)
注意:as 後面首先跟着元素,然後才是下標。而且元素和下標不需要用括號括起來。
如果元素是對象,可以解構
如果源數據沒有內容,是空數組的情況下,還可以組合 {:else} 一起使用。

使用 on: 指令監聽 DOM 事件,on: 後面跟隨事件類型
語法:
on:事件類型={事件名}舉個例子,點擊按鈕時在控制台輸出 「雷猴」。

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

從上圖可以看出,多次點擊都只是輸出 1 次「雷猴」。
除了 once 之外,還有以下這些修飾符可以用:
修飾符還可以串聯起來使用,比如 on:click|once|capture={...}
但需要注意,有些特殊的標籤使用修飾符會出現「意想不到」的結果,比如 <a> 標籤。

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

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

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

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



multiple 和 checkbox 有點像。

如果 bind 綁定的屬性和在 JS 里聲明的變量名相同,那可以直接綁定

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

使用 $: 聲明的 double 會自動根據 count 的值改變而改變。
如果將以上代碼中 $: 改成 let 或者 var 聲明 count ,那麼 count 將失去響應性。
這樣看來,真的和 Vue 的 computed 的作用有那麼一點像。
異步渲染 #awaitSvelte 提供異步渲染標籤,可以提升用戶體驗。
語法:
{#awaitexpression}...{:thenname}...{:catchname}...{/await}以 #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
<script>importPhonefrom'./Phone.svelte'</script><div>子組件 Phone 的內容:</div><Phone/>Phone.svelte
<div>電話:13266668888</div>組件通訊組件通訊主要是 父子組件 之間的數據來往。
父傳子比如上面的例子,手機號希望從 App.svelte 組件往 Phone.svelte 里傳。
可以在 Phone.svelte 中聲明一個變量,並公開該變量。
App.svelte 就可以使用對應的屬性把值傳入。

App.svelte
<script>importPhonefrom'./Phone.svelte'</script><div>子組件 Phone 的內容:</div><Phonenumber="88888888"/>Phone.svelte
<script> export let number = '13266668888'</script><div>電話:{number}</div>如果此時 App.svelte 組件沒有傳值進來,Phone.svelte 就會使用默認值。
子傳父如果想在子組件中修改父組件的內容,需要把修改的方法定義在父組件中,並把該方法傳給子組件調用。
同時需要在子組件中引入 createEventDispatcher 方法。

App.svelte
<script>importPhonefrom'./Phone.svelte'functionprint(data){console.log(`手機號:${data.detail}`)}</script><div>子組件 Phone 的內容:</div><Phoneon:printPhone={print}/>Phone.svelte
<script>import{createEventDispatcher}from'svelte'constdispatch=createEventDispatcher()functionprintPhone(){dispatch('printPhone','13288888888')}</script><buttonon:click={printPhone}>輸出手機號</button>父組件接受參數是一個對象,子組件傳過來的值都會放在 detail 屬性里。
插槽 slot和 Vue 一樣,Svelte 也有組件插槽。
在子組件中使用 <slot> 標籤,可以接收父組件傳進來的 HTML 內容。

App.svelte
<script>importPhonefrom'./Phone.svelte'</script><div>子組件 Phone 的內容:</div><Phone><div>電話:</div><div>13288889999</div></Phone>Phone.svelte
<style>.box{width:100px;border:1pxsolid#aaa;border-radius:8px;box-shadow:2px2px8pxrgba(0,0,0,0.1);padding:1em;margin:1em0;}</style><divclass="box"><slot>默認值</slot></div>生命周期生命周期是指項目運行時,指定時期會自動執行的方法。
Svelte 中主要有以下幾個生命周期:
以上生命周期都是需要從 svelte 里引入的。
用 onMount 舉個例子

在組件加載完 1 秒後,改變 title 的值。
onDestroy、beforeUpdate 和 afterUpdate 都和 onMount 的用法差不多,只是執行的時間條件不同。你可以自己創建個項目試試看。
tick 是比較特殊的,tick 和 Vue 的 nextTick 差不多。
在 Svelte 中,tick 的使用語法如下:
import{tick}from'svelte'awaittick()//其他操作總結本文主要講解了 Svelte 的基礎用法,但 Svelte 的內容和 API 遠不止此。它還有很多高級的用法以及提供了過渡動畫功能等,這些都會放在高級篇講解。
Svelte 是一個 Web 應用的構建工具,它打包出來的項目體積比較小,性能強,不使用虛擬 DOM。
但 Svelte 的兼容性和周邊生態相比起 Vue 和 React 會差一點。
所以日常項目中需要根據 Svelte 的優缺點進行取捨。
推薦閱讀推薦書籍
推薦文章
點讚 + 關注 + 收藏 = 學會了
參考資料https://svelte.dev/: https://svelte.dev/
[2]https://svelte.dev/: https://svelte.dev/
[3]https://www.vitejs.net/guide/#trying-vite-online: https://www.vitejs.net/guide/#trying-vite-online
[4]https://vite.new/vanilla: https://vite.new/vanilla
[5]https://vite.new/vanilla-ts: https://vite.new/vanilla-ts
[6]https://vite.new/vue: https://vite.new/vue
[7]https://vite.new/vue-ts: https://vite.new/vue-ts
[8]https://vite.new/react: https://vite.new/react
[9]https://vite.new/react-ts: https://vite.new/react-ts
[10]https://vite.new/preact: https://vite.new/preact
[11]https://vite.new/preact-ts: https://vite.new/preact-ts
[12]https://vite.new/lit: https://vite.new/lit
[13]https://vite.new/lit-ts: https://vite.new/lit-ts
[14]https://vite.new/svelte: https://vite.new/svelte
[15]https://vite.new/svelte-ts: https://vite.new/svelte-ts
[16]https://svelte.dev/tutorial/basics: https://svelte.dev/tutorial/basics
[17]https://svelte.dev/tutorial/basics: https://svelte.dev/tutorial/basics
[18]https://www.sveltejs.cn/: https://www.sveltejs.cn/
[19]https://svelte.dev/: https://svelte.dev/
[20]https://github.com/Rich-Harris: https://github.com/Rich-Harris
[21]https://github.com/rollup/rollup: https://github.com/rollup/rollup
[22]https://github.com/rollup/rollup: https://github.com/rollup/rollup
[23]https://ractive.js.org/: https://ractive.js.org/
[24]https://survivejs.com/blog/svelte-interview/: https://survivejs.com/blog/svelte-interview/
[25]https://svelte.dev/: https://svelte.dev/
[26]https://www.freecodecamp.org/news/a-realworld-comparison-of-front-end-frameworks-with-benchmarks-2019-update-4be0d3c78075: https://www.freecodecamp.org/news/a-realworld-comparison-of-front-end-frameworks-with-benchmarks-2019-update-4be0d3c78075
[27]https://www.sveltejs.cn/blog/virtual-dom-is-pure-overhead: https://www.sveltejs.cn/blog/virtual-dom-is-pure-overhead
[28]https://juejin.cn/user/1081575169076430: https://juejin.cn/user/1081575169076430
[29]https://juejin.cn/post/6965741667928244254#heading-19: https://juejin.cn/post/6965741667928244254#heading-19
[30]https://www.sveltejs.cn/blog/svelte-3-rethinking-reactivity: https://www.sveltejs.cn/blog/svelte-3-rethinking-reactivity
[31]https://krausest.github.io/js-framework-benchmark/current.html: https://krausest.github.io/js-framework-benchmark/current.html
[32]https://krausest.github.io/js-framework-benchmark/current.html: https://krausest.github.io/js-framework-benchmark/current.html
[33]https://krausest.github.io/js-framework-benchmark/current.html: https://krausest.github.io/js-framework-benchmark/current.html
[34]https://sapper.svelte.dev/: https://sapper.svelte.dev/
[35]https://svelte-native.technology/: https://svelte-native.technology/
[36]https://nativescript.org/: https://nativescript.org/
[37]https://github.com/sveltejs/gl: https://github.com/sveltejs/gl
[38]https://threejs.org/: https://threejs.org/
[39]https://nodejs.org/zh-cn/: https://nodejs.org/zh-cn/
[40]https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode: https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode
[41]https://www.sveltejs.cn/: https://www.sveltejs.cn/
[42]https://www.sveltejs.cn/repl/hello-world: https://www.sveltejs.cn/repl/hello-world
[43]https://github.com/Rich-Harris: https://github.com/Rich-Harris
[44]https://github.com/DeMoorJasper/parcel-plugin-svelte: https://github.com/DeMoorJasper/parcel-plugin-svelte
[45]https://github.com/DeMoorJasper/parcel-plugin-svelte: https://github.com/DeMoorJasper/parcel-plugin-svelte
[46]https://juejin.cn/post/7112204639029690381: https://juejin.cn/post/7112204639029690381
[47]https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label
[48]https://juejin.cn/post/7116784455561248775: https://juejin.cn/post/7116784455561248775
[49]https://juejin.cn/post/7118985770408345630: https://juejin.cn/post/7118985770408345630
[50]https://juejin.cn/post/7026941253845516324: https://juejin.cn/post/7026941253845516324
[51]https://juejin.cn/post/7069251532339806238: https://juejin.cn/post/7069251532339806238
[52]https://juejin.cn/post/6915047570226020359: https://juejin.cn/post/6915047570226020359
關於本文作者:德育處主任。https://juejin.cn/post/7121759118070644772#comment
」