新興前端框架 Svelte 從入門到原理
在這篇文章中,我們將會(huì)介紹 Svelte 框架的特性、優(yōu)缺點(diǎn)和底層原理。
本文盡量不會(huì)涉及 Svelte 的語法,大家可以放心食用。因?yàn)?Svelte 的語法極其簡(jiǎn)單,而且官方教程學(xué)習(xí)曲線平緩https://www.sveltejs.cn/,相信大家很快就會(huì)上手語法的,這里就不做官網(wǎng)搬運(yùn)工了。
前端領(lǐng)域是發(fā)展迅速,各種輪子層出不窮的行業(yè)。最近這些年,隨著三大框架React、Vue、Angular版本逐漸穩(wěn)定,前端技術(shù)棧的迭代似乎緩慢下來,React 16版本推出了 Fiber, Vue 3.0 也已經(jīng)在襁褓之中。
如果我們把目光拉伸到未來十年的視角,前端行業(yè)會(huì)出現(xiàn)哪些框架有可能會(huì)挑戰(zhàn)React或者Vue呢?我們認(rèn)為,嶄露頭角的 Svelte 應(yīng)該是其中的選項(xiàng)之一。
Svelte 簡(jiǎn)介
Svelte叫法是[Svelte], 本意是苗條纖瘦的,是一個(gè)新興熱門的前端框架。

在最新的《State of JS survey of 2020》中,它被預(yù)測(cè)為未來十年可能取代React和Vue等其他框架的新興技術(shù)。如果你不確定自己是否該了解 Svelte,可以先看一下 Svelte 的一些發(fā)展趨勢(shì)。
開發(fā)者滿意度
從2019年開始, Svelte出現(xiàn)在榜單中。剛剛過去的2020年,Svelte在滿意度排行榜中超越了react,躍升到了第一位。

開發(fā)者興趣度
在開發(fā)者興趣度方面,Svelte 蟬聯(lián)了第一。

市場(chǎng)占有率
如果你在19年還沒有聽說過Svelte,不用緊張,因?yàn)?code style="font-size: 14px;word-wrap: break-word;border-radius: 4px;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #377dca;background-color: #fff5e3;padding: 3px;margin: 3px;">svelte 當(dāng)時(shí)仍是小眾的開發(fā)框架,在社區(qū)里仍然沒有流行開來。

2020年,Svelte 的市場(chǎng)占有率從第6名躍升到第4名,僅次于 React、Angular、Vue 老牌前端框架。
svelte作者——Rich Harris

Svelte作者是前端輪子哥 Rich Harris,同時(shí)也是 Rollup 的作者。Rich Harris 作者本人在介紹 Svelte 時(shí),有一個(gè)非常精彩的演講《Rethinking reactivity》,油管連接:https://www.youtube.com/watch?v=AdNJ3fydeao&t=1900s,感興趣的同學(xué)不要錯(cuò)過。
他設(shè)計(jì) Svelte 的核心思想在于『通過靜態(tài)編譯減少框架運(yùn)行時(shí)的代碼量』,也就是說,vue 和 react 這類傳統(tǒng)的框架,都必須引入運(yùn)行時(shí) (runtime) 代碼,用于虛擬dom、diff 算法。Svelted完全溶入JavaScript,應(yīng)用所有需要的運(yùn)行時(shí)代碼都包含在bundle.js里面了,除了引入這個(gè)組件本身,你不需要再額外引入一個(gè)運(yùn)行代碼。
Svelte 優(yōu)勢(shì)有哪些
我們先來看一下 Svelte 和React,Vue 相比,有哪些優(yōu)勢(shì)。
No Runtime —— 無運(yùn)行時(shí)代碼
React 和 Vue 都是基于運(yùn)行時(shí)的框架,當(dāng)用戶在你的頁(yè)面進(jìn)行各種操作改變組件的狀態(tài)時(shí),框架的運(yùn)行時(shí)會(huì)根據(jù)新的組件狀態(tài)(state)計(jì)算(diff)出哪些DOM節(jié)點(diǎn)需要被更新,從而更新視圖。
這就意味著,框架本身所依賴的代碼也會(huì)被打包到最終的構(gòu)建產(chǎn)物中。這就不可避免增加了打包后的體積,有一部分的體積增加是不可避免的,那么這部分體積大約是多少呢?請(qǐng)看下面的數(shù)據(jù):

常用的框架中,最小的Vue都有58k,React更有97.5k。我們使用React開發(fā)一個(gè)小型組件,即使里面的邏輯代碼很少,但是打包出來的bundle size輕輕松松都要100k起步。對(duì)于大型后臺(tái)管理系統(tǒng)來說,100k 不算什么,但是對(duì)于特別注重用戶端加載性能的場(chǎng)景來說,一個(gè)組件100k 多,還是太大了。
如果你特別在意打包出來的體積,Svelte 就是一個(gè)特別好的選擇。下面是Jacek Schae大神的統(tǒng)計(jì),使用市面上主流的框架,來編寫同樣的Realword 應(yīng)用的體積:

從上圖的統(tǒng)計(jì),Svelte簡(jiǎn)直是神奇!竟然只有 9.7 KB ! 果然魔法消失 UI 框架,無愧其名。
可以看出,Svelte的bundle size大小是Vue的1/4,是React的1/20,體積上的優(yōu)勢(shì)還是相當(dāng)明顯的。
Less-Code ——寫更少的代碼
在寫svelte組件時(shí),你就會(huì)發(fā)現(xiàn),和 Vue 或 React 相比只需要更少的代碼。開發(fā)者的夢(mèng)想之一,就是敲更少的代碼。因?yàn)楦俚拇a量,往往意味著有更好的語義性,也有更少的幾率寫出bug。
下面的例子,可以看出Svelte和React的不同:
React的代碼
const [count, setCount] = useState(0);
function increment() {
setCount(count + 1);
}
Svelte的代碼
let count = 0;
function increment() {
count += 1;
}
雖然用上了16版本最新的 hooks,但是和svelte相比,代碼還是很冗余。
在React中,我們要么使用useState鉤子,要么使用setState設(shè)置狀態(tài)。而在Svelte中,可以直接使用賦值操作符更新狀態(tài)。
如果說上面的例子太簡(jiǎn)單了,可以看下面的統(tǒng)計(jì),分別使用 React 和 Svelte 實(shí)現(xiàn)下面的組件所需要的代碼行數(shù)

下面還是 Jacek Schae 老哥的統(tǒng)計(jì),編寫同樣的Realword 應(yīng)用,各個(gè)框架所需要的行數(shù)

Vue 和 React 打了平手,Svelte 遙遙領(lǐng)先,可以少些 1000 行代碼耶!早日下班,指日可待。
Hight-Performance ——高性能
在Virtual Dom已經(jīng)是前端框架標(biāo)配的今天, Svelte 聲稱自己是沒有Virtual Dom加持的, 怎么還能保證高性能呢?
不急,慢慢看。
性能測(cè)評(píng)
Jacek Schae 在《A RealWorld Comparison of Front-End Frameworks with Benchmarks》中用主流的前端框架來編寫 RealWorld 應(yīng)用,使用 Chrome 的Lighthouse Audit測(cè)試性能,得出數(shù)據(jù)是Svelte 略遜于Vue, 但好于 React。

是不是很驚奇?另外一個(gè)前端框架性能對(duì)比的項(xiàng)目也給出了同樣的答案:https://github.com/krausest/js-framework-benchmark。

為什么 Svelte 性能還不錯(cuò),至少?zèng)]有我們預(yù)期的那么糟糕?我們接下來會(huì)在原理那一小結(jié)來介紹。
Svelte 劣勢(shì)
說完了 Svelte 的優(yōu)勢(shì),我們也要考慮到 Svelte 的劣勢(shì)。
和Vue, React框架的對(duì)比
在構(gòu)建大型前端項(xiàng)目時(shí),我們?cè)谶x擇框架的時(shí)候就需要考慮更多的事情。Svelte 目前尚處在起步階段,對(duì)于大型項(xiàng)目必要的單元測(cè)試并沒有完整的方案。目前在大型應(yīng)用中使用 Svelte , 需要謹(jǐn)慎評(píng)。
| 類目 | Svelte | Vue | React |
|---|---|---|---|
| UI 組件庫(kù) | Material design ( 坦率的說,不好用 ) | Element UI / AntD | AntD / Material design |
| 狀態(tài)管理 | 官網(wǎng)自帶 | Vuex | Redux/MobX |
| 路由 | Svelte-router | Vue-router | React-router |
| 服務(wù)端渲染 | 支持 | 支持 | 支持 |
| 測(cè)試工具 | 官方網(wǎng)站沒有相關(guān)內(nèi)容 | @vue/test-utils | Jest |
我們?cè)谟?Svelte 開發(fā)公司級(jí)別中大型項(xiàng)目時(shí),也發(fā)現(xiàn)了其他的一些主要注意的點(diǎn)
沒有像AntD那樣成熟的UI庫(kù)。比如說需求方想加一個(gè)toast提示,或者彈窗,pm:”很簡(jiǎn)單的,不用出UI稿,就直接用之前的樣式好啦~“
但是 Svelte 需要從0開始 ”抄“ 出來一個(gè)toast或者彈窗組件出來,可能會(huì)帶來額外的開發(fā)量和做好加班的準(zhǔn)備。
Svelte 原生不支持預(yù)處理器,比如說
less/scss,需要自己?jiǎn)为?dú)的配置 webpack loader。Svelte 原生腳手架沒有目錄劃分
暫時(shí)不支持typescript,雖然官方說了會(huì)支持, 但是不知道什么時(shí)候.
還需要注意的一點(diǎn)是,React / Vue等框架自帶的runtime雖然會(huì)增加首屏加載的bundle.js,可是當(dāng)項(xiàng)目變得越來越大的時(shí)候,框架的runtime在bundle.js里面占據(jù)的比例也會(huì)越來越小,這個(gè)時(shí)候我們就得考慮一下是不是存在一個(gè)Svelte生成的代碼大于React和Vue生成的代碼的閾值了。
原理篇
Svelte 原理相對(duì)于 React 和 Vue 來說,相對(duì)比較簡(jiǎn)單,大家可以放心的往下看。
首先,我們從一個(gè)問題出發(fā):
Virtual Dom 真的高效嗎
Rich Harris 在設(shè)計(jì) Svelte 的時(shí)候沒有采用 Virtual DOM 是因?yàn)橛X得Virtual DOM Diff 的過程是非常低效的。
在他的一文《Virtual DOM is pure overhead》原文連接:https://www.sveltejs.cn/blog/virtual-dom-is-pure-overhead,感興趣的同學(xué)可以翻一下。
人們覺得 Virtual DOM高效的一個(gè)理由,就是它不會(huì)直接操作原生的DOM節(jié)點(diǎn)。在瀏覽器當(dāng)中,JavaScript的運(yùn)算在現(xiàn)代的引擎中非常快,但DOM本身是非常緩慢的東西。當(dāng)你調(diào)用原生DOM API的時(shí)候,瀏覽器需要在JavaScript引擎的語境下去接觸原生的DOM的實(shí)現(xiàn),這個(gè)過程有相當(dāng)?shù)男阅軗p耗。
但其實(shí) Virtual DOM 有時(shí)候會(huì)做很多無用功,這體現(xiàn)在很多組件會(huì)被“無緣無故”進(jìn)行重渲染(re-render)。
比如說,下面的例子中,React 為了更新掉message 對(duì)應(yīng)的DOM 節(jié)點(diǎn),需要做n多次遍歷,才能找到具體要更新哪些節(jié)點(diǎn)。

為了解決這個(gè)問題,React 提供pureComponent,shouldComponentUpdate,useMemo,useCallback讓開發(fā)者來操心哪些subtree是需要重新渲染的,哪些是不需要重新渲染的。究其本質(zhì),是因?yàn)?React 采用 jsx 語法過于靈活,不理解開發(fā)者寫出代碼所代表的意義,沒有辦法做出優(yōu)化。
所以,React 為了解決這個(gè)問題,在 v16.0 帶來了全新的 Fiber 架構(gòu),F(xiàn)iber 思路是不減少渲染工作量,把渲染工作拆分成小任務(wù)思路是不減少渲染工作量。渲染過程中,留出時(shí)間來處理用戶響應(yīng),讓用戶感覺起來變快了。這樣會(huì)帶來額外的問題,不得不加載額外的代碼,用于處理復(fù)雜的運(yùn)行時(shí)調(diào)度工作
那么 Svelte 是如何解決這個(gè)問題的?
React 采用 jsx 語法本質(zhì)不理解數(shù)據(jù)代表的意義,沒有辦法做出優(yōu)化。Svelte 采用了Templates語法(類似于 Vue 的寫法),更加嚴(yán)格和具有語義性,可以在編譯的過程中就進(jìn)行優(yōu)化操作。
那么,為什么Templates語法可以解決這個(gè)問題呢?
Template 帶來的優(yōu)勢(shì)
關(guān)于 JSX 與 Templates ,可以看成是兩種不同的前端框架渲染機(jī)制,有興趣的同學(xué)可以翻一下尤雨溪的演講《在框架設(shè)計(jì)中尋求平衡》:https://www.bilibili.com/video/av80042358/。
一方面, JSX 的代表框架有 React 以及所有 react-like 庫(kù),比如 preact、 stencil, infernal 等;另一方面, Templates 代表性的解決方案有 Vue、Svelte、 ember,各有優(yōu)缺點(diǎn)。

JSX 優(yōu)缺點(diǎn)
jsx 具有 JavaScript 的完整表現(xiàn)力,非常具有表現(xiàn)力,可以構(gòu)建非常復(fù)雜的組件。
但是靈活的語法,也意味著引擎難以理解,無法預(yù)判開發(fā)者的用戶意圖,從而難以優(yōu)化性能。你很可能會(huì)寫出下面的代碼:

在使用 JavaScript 的時(shí)候,編譯器不可能hold住所有可能發(fā)生的事情,因?yàn)?JavaScript 太過于動(dòng)態(tài)化。也有人對(duì)這塊做了很多嘗試,但從本質(zhì)上來說很難提供安全的優(yōu)化。
Template優(yōu)缺點(diǎn)
Template模板是一種非常有約束的語言,你只能以某種方式去編寫模板。
例如,當(dāng)你寫出這樣的代碼的時(shí)候,編譯器可以立刻明白:”哦!這些 p 標(biāo)簽的順序是不會(huì)變的,這個(gè) id 是不會(huì)變的,這些 class 也不會(huì)變的,唯一會(huì)變的就是這個(gè)“。

在編譯時(shí),編譯器對(duì)你的意圖可以做更多的預(yù)判,從而給它更多的空間去做執(zhí)行優(yōu)化。

左側(cè) template 中,其他所有內(nèi)容都是靜態(tài)的,只有 name 可能會(huì)發(fā)生改變。
右側(cè) p 函數(shù)是編譯生成的最終的產(chǎn)物,是原生的js可以直接運(yùn)行在瀏覽器里,會(huì)在有臟數(shù)據(jù)時(shí)被調(diào)用。p 函數(shù)唯一做的事情就是,當(dāng) name 發(fā)生變更的時(shí)候,調(diào)用原生方法把 t1 這個(gè)原生DOM節(jié)點(diǎn)更新。這里的 set_data 可不是 React 的 setState 或者小程序的 setData ,這里的set_data 就是封裝的原生的 javascript 操作DOM 節(jié)點(diǎn)的方法。

如果我們仔細(xì)觀察上面的代碼,發(fā)現(xiàn)問題的關(guān)鍵在于 if 語句的判斷條件——changed.name, 表示有哪些變量被更新了,這些被更新的變量被稱為臟數(shù)據(jù)。
任何一個(gè)現(xiàn)代前端框架,都需要記住哪些數(shù)據(jù)更新了,根據(jù)更新后的數(shù)據(jù)渲染出最新的DOM
Svelte 記錄臟數(shù)據(jù)的方式:位掩碼(bitMask)
Svelte使用位掩碼(bitMask) 的技術(shù)來跟蹤哪些值是臟的,即自組件最后一次更新以來,哪些數(shù)據(jù)發(fā)生了哪些更改。
位掩碼是一種將多個(gè)布爾值存儲(chǔ)在單個(gè)整數(shù)中的技術(shù),一個(gè)比特位存放一個(gè)數(shù)據(jù)是否變化,一般1表示臟數(shù)據(jù),0表示是干凈數(shù)據(jù)。

用大白話來講,你有A、B、C、D 四個(gè)值,那么二進(jìn)制0000 0001表示第一個(gè)值A發(fā)生了改變,0000 0010表示第二個(gè)值B發(fā)生了改變,0000 0100表示第三個(gè)值C發(fā)生了改變,0000 1000表示第四個(gè)D發(fā)生了改變。
這種表示法,可以最大程度的利用空間。為啥這么說呢?
比如說,十進(jìn)制數(shù)字3就可以表示 A、B是臟數(shù)據(jù)。先把十進(jìn)制數(shù)字3, 轉(zhuǎn)變?yōu)槎M(jìn)制0000 0011。從左邊數(shù)第一位、第二位是1,意味著第一個(gè)值A(chǔ) 和第二個(gè)值B是臟數(shù)據(jù);其余位都是0,意味著其余數(shù)據(jù)都是干凈的。

JS 的限制
那么,是不是用二進(jìn)制比特位就可以記錄各種無窮無盡的變化了呢?
JS 的二進(jìn)制有31位限制,number 類型最長(zhǎng)是32位,減去1位用來存放符號(hào)。也就是說,如果 Svelte 采用二進(jìn)制位存儲(chǔ)的方法,那么只能存 31個(gè)數(shù)據(jù)。
但肯定不能這樣,對(duì)吧?
Svelte 采用數(shù)組來存放,數(shù)組中一項(xiàng)是二進(jìn)制31位的比特位。假如超出31個(gè)數(shù)據(jù)了,超出的部分放到數(shù)組中的下一項(xiàng)。
這個(gè)數(shù)組就是component.$.dirty數(shù)組,二進(jìn)制的1位表示該對(duì)應(yīng)的數(shù)據(jù)發(fā)生了變化,是臟數(shù)據(jù),需要更新;二進(jìn)制的0位表示該對(duì)應(yīng)的數(shù)據(jù)沒有發(fā)生變化,是干凈的。
一探究竟component.$.dirty
上文中,我們說到component.$.dirty是數(shù)組,具體這個(gè)數(shù)組長(zhǎng)什么樣呢?
我們模擬一個(gè) Svelte 組件,這個(gè) Svelte 組件會(huì)修改33個(gè)數(shù)據(jù)。
我們打印出每一次make_dirty之后的component.$.dirty, 為了方便演示,轉(zhuǎn)化為二進(jìn)制打印出來,如下面所示:

上面數(shù)組中的每一項(xiàng)中的每一個(gè)比特位,如果是1,則代表著該數(shù)據(jù)是否是臟數(shù)據(jù)。如果是臟數(shù)據(jù),則意味著更新。
第一行
["0000000000000000000000000000001", "0000000000000000000000000000000"], 表示第一個(gè)數(shù)據(jù)臟了,需要更新第一個(gè)數(shù)據(jù)對(duì)應(yīng)的dom節(jié)點(diǎn)第二行
["0000000000000000000000000000011", "0000000000000000000000000000000"], 表示第一個(gè)、第二個(gè)數(shù)據(jù)都臟了,需要更新第一個(gè),第二個(gè)數(shù)據(jù)對(duì)應(yīng)的dom節(jié)點(diǎn)。……
當(dāng)一個(gè)組件內(nèi),數(shù)據(jù)的個(gè)數(shù),超出了31的數(shù)量限制,就數(shù)組新增一項(xiàng)來表示。
這樣,我們就可以通過component.$.dirty這個(gè)數(shù)組,清楚的知道有哪些數(shù)據(jù)發(fā)生了變化。那么具體應(yīng)該更新哪些DOM 節(jié)點(diǎn)呢?
數(shù)據(jù)和DOM節(jié)點(diǎn)之間的對(duì)應(yīng)關(guān)系
我們都知道, React 和 Vue 是通過 Virtual Dom 進(jìn)行 diff 來算出來更新哪些 DOM 節(jié)點(diǎn)效率最高。Svelte 是在編譯時(shí)候,就記錄了數(shù)據(jù) 和 DOM 節(jié)點(diǎn)之間的對(duì)應(yīng)關(guān)系,并且保存在 p 函數(shù)中。

這里說的p 函數(shù),就是 Svelte 的更新方法,本質(zhì)上就是一大堆if判斷,邏輯非常簡(jiǎn)單
if ( A 數(shù)據(jù)變了 ) {
更新A對(duì)應(yīng)的DOM節(jié)點(diǎn)
}
if ( B 數(shù)據(jù)變了 ) {
更新B對(duì)應(yīng)的DOM節(jié)點(diǎn)
}
為了更加直觀的理解,我們模擬更新一下33個(gè)數(shù)據(jù)的組件,編譯得到的p 函數(shù)打印出來,如:

我們會(huì)發(fā)現(xiàn),里面就是一大堆if判斷,但是if判斷條件比較有意思,我們從上面摘取一行仔細(xì)觀察一下:

首先要注意,&不是邏輯與,而是按位與,會(huì)把兩邊數(shù)值轉(zhuǎn)為二進(jìn)制后進(jìn)行比較,只有相同的二進(jìn)制位都為1 才會(huì)為真。
這里的if判斷條件是:拿compoenent.$.dirty[0](00000000000000000000000000000100)和4(4 轉(zhuǎn)變?yōu)槎M(jìn)制是0000 0100)做按位并操作。那么我們可以思考一下了,這個(gè)按位并操作什么時(shí)候會(huì)返回1呢?
4是一個(gè)常量,轉(zhuǎn)變?yōu)槎M(jìn)制是0000 0100, 第三位是1。那么也就是,只有dirty[0]的二進(jìn)制的第三位也是1時(shí), 表達(dá)式才會(huì)返回真。換句話來說,只有第三個(gè)數(shù)據(jù)是臟數(shù)據(jù),才會(huì)走入到這個(gè)if判斷中,執(zhí)行set_data(t5, ctx[2]), 更新t5這個(gè) DOM 節(jié)點(diǎn)。
當(dāng)我們分析到這里,已經(jīng)看出了一些眉目,讓我們站在更高的一個(gè)層次去看待這 30多行代碼:它們其實(shí)是保存了這33個(gè)變量 和 真實(shí)DOM 節(jié)點(diǎn)之間的對(duì)應(yīng)關(guān)系,哪些變量臟了,Svelte 會(huì)走入不同的if體內(nèi)直接更新對(duì)應(yīng)的DOM節(jié)點(diǎn),而不需要復(fù)雜 Virtual DOM DIFF 算出更新哪些DOM節(jié)點(diǎn);
這 30多行代碼,是Svelte 編譯了我們寫的Svelte 組件之后的產(chǎn)物,在Svelte 編譯時(shí),就已經(jīng)分析好了,數(shù)據(jù) 和 DOM 節(jié)點(diǎn)之間的對(duì)應(yīng)關(guān)系,在數(shù)據(jù)發(fā)生變化時(shí),可以非常高效的來更新DOM節(jié)點(diǎn)。
Vue 曾經(jīng)也是想采取這樣的思路,但是 Vue 覺得保存每一個(gè)臟數(shù)據(jù)太消耗內(nèi)存了,于是沒有采用那么細(xì)顆粒度,而是以組件級(jí)別的中等顆粒度,只監(jiān)聽到組件的數(shù)據(jù)更新,組件內(nèi)部再通過 DIFF 算法計(jì)算出更新哪些 DOM 節(jié)點(diǎn)。Svelte 采用了比特位的存儲(chǔ)方式,解決了保存臟數(shù)據(jù)會(huì)消耗內(nèi)存的問題。
整體流程
上面就是Svelte 最核心更新DOM機(jī)制,下面我們串起來整個(gè)的流程。
下面是非常簡(jiǎn)單的一個(gè) Svelte 組件,點(diǎn)擊<button>會(huì)觸發(fā)onClick事件,從而改變name 變量。

上面代碼背后的整體流程如下圖所示,我們一步一步來看:

第一步,Svelte 會(huì)編譯我們的代碼,下圖中左邊是我們的源碼,右邊是 Svelte 編譯生成的。Svelte 在編譯過程中發(fā)現(xiàn),『咦,這里有一行代碼 name 被重新賦值了,我要插入一條make_dirty的調(diào)用』,于是當(dāng)我們改寫 name 變量的時(shí)候,就會(huì)調(diào)用make_dirty方法把 name 記為臟數(shù)據(jù)。

第二步,我們來看make_diry方法究竟做了什么事情:
把對(duì)應(yīng)數(shù)據(jù)的二進(jìn)制改為1
把對(duì)應(yīng)組件記為臟組件,推入到 dirty_components 數(shù)組中
調(diào)用
schedule_update()方法把flush方法推入到一幀中的微任務(wù)階段執(zhí)行。因?yàn)檫@樣既可以做頻繁更新 的截流,又避免了阻塞一幀中的 layout, repaint 階段的渲染。

schedule_update 方法其實(shí)就是一個(gè)promise.then(),

一幀大概有 16ms, 大概會(huì)經(jīng)歷 layout, repaint的階段后,就可以開始執(zhí)行微任務(wù)的回調(diào)了。
flush 方法做的事情也比較簡(jiǎn)單,就是遍歷臟組件,依次調(diào)用update方法去更新對(duì)應(yīng)的組件。

update方法除了執(zhí)行一些生命周期的方法外,最核心的一行代碼是調(diào)用p方法,p方法我們已經(jīng)在上文中介紹過很熟悉了。

p 方法的本質(zhì)就是走入到不同的if 判斷里面,調(diào)用set_data原生的 javascript 方法更新對(duì)應(yīng)的 DOM節(jié)點(diǎn)。

至此,我們的頁(yè)面的DOM節(jié)點(diǎn)就已經(jīng)更新好了。
上面的代碼均是剔除了分支邏輯的偽代碼。
Svelte 在處理子節(jié)點(diǎn)列表的時(shí)候,還是有優(yōu)化的算法在的。比如說[a,b,c,d] 變成 [d, a, b, c] ,但是只是非常簡(jiǎn)單的優(yōu)化,簡(jiǎn)單來說,是比較節(jié)點(diǎn)移動(dòng)距離的絕對(duì)值,絕對(duì)值最小的節(jié)點(diǎn)被移動(dòng)。
所以,嚴(yán)格意義上來說,Svelte 并不是100%無運(yùn)行時(shí),還是會(huì)引入額外的算法邏輯,只是量很少罷了。
總結(jié)
一個(gè)前端框架,不管是vue還是react更新了數(shù)據(jù)之后,需要考慮更新哪個(gè)dom節(jié)點(diǎn),也就是,需要知道,臟數(shù)據(jù)和待更新的真實(shí)dom之間的映射。vue, react 是通過 virtualDom 來 diff 計(jì)算出更新哪些dom節(jié)點(diǎn)更劃算,而sveltedom 是把數(shù)據(jù)和真實(shí)dom之間的映射關(guān)系,在編譯的時(shí)候就通過AST等算出來,保存在p函數(shù)中。
Svelte 作為新興的前端框架,采用了和 React, Vue 不同的設(shè)計(jì)思路,其獨(dú)特的特性在某些場(chǎng)景下還是很值得嘗試的。
