「轉」新興前端框架 Svelte?能否超越ReactVueAngular?

在這篇文章中,我們將會介紹 Svelte 框架的特性、優缺點和底層原理。

前端領域是發展迅速,各種輪子層出不窮的行業。最近這些年,隨著三大框架React、Vue、Angular版本逐漸穩定,前端技術棧的迭代似乎緩慢下來,React 16版本推出了 Fiber, Vue 3。0 也已經在襁褓之中。

如果我們把目光拉伸到未來十年的視角,前端行業會出現哪些框架有可能會挑戰React或者Vue呢?我們認為,嶄露頭角的 Svelte 應該是其中的選項之一。

1。Svelte 簡介

Svelte叫法是[Svelte], 本意是苗條纖瘦的,是一個新興熱門的前端框架。

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

在最新的《State of JS survey of 2020》中,它被預測為未來十年可能取代React和Vue等其他框架的新興技術。如果你不確定自己是否該瞭解 Svelte,可以先看一下 Svelte 的一些發展趨勢。

2。開發者滿意度

從2019年開始, Svelte出現在榜單中。剛剛過去的2020年,Svelte在滿意度排行榜中超越了react,躍升到了第一位。

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

3。開發者興趣度

在開發者興趣度方面,Svelte 蟬聯了第一。

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

4。市場佔有率

如果你在19年還沒有聽說過Svelte,不用緊張,因為svelte 當時仍是小眾的開發框架,在社群裡仍然沒有流行開來。

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

2020年,Svelte 的市場佔有率從第6名躍升到第4名,僅次於 React、Angular、Vue 老牌前端框架。

5。Svelte作者——Rich Harris

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

Svelte作者是前端輪子哥 Rich Harris,同時也是 Rollup 的作者。Rich Harris 作者本人在介紹 Svelte 時,有一個非常精彩的演講《Rethinking reactivity》,油管連線:https://www。youtube。com/watch?v=AdNJ3fydeao&t=1900s,感興趣的同學不要錯過。

他設計 Svelte 的核心思想在於『

透過靜態編譯減少框架執行時的程式碼量

』,也就是說,vue 和 react 這類傳統的框架,都必須引入執行時 (runtime) 程式碼,用於虛擬dom、diff 演算法。Svelted完全溶入JavaScript,應用所有需要的執行時程式碼都包含在bundle。js裡面了,除了引入這個元件本身,你不需要再額外引入一個執行程式碼。

5。Svelte 優勢有哪些

我們先來看一下 Svelte 和React,Vue 相比,有哪些優勢。

5。1 No Runtime —— 無執行時程式碼

React 和 Vue 都是基於執行時的框架,當用戶在你的頁面進行各種操作改變元件的狀態時,框架的執行時會根據新的元件狀態(state)計算(diff)出哪些DOM節點需要被更新,從而更新檢視。

這就意味著,框架本身所依賴的程式碼也會被打包到最終的構建產物中。這就不可避免增加了打包後的體積,有一部分的體積增加是不可避免的,那麼這部分體積大約是多少呢?請看下面的資料:

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

常用的框架中,最小的Vue都有58k,React更有97。5k。我們使用React開發一個小型元件,即使裡面的邏輯程式碼很少,但是打包出來的bundle size輕輕鬆鬆都要100k起步。對於大型後臺管理系統來說,100k 不算什麼,但是對於特別注重使用者端載入效能的場景來說,一個元件100k 多,還是太大了。

如果你特別在意打包出來的體積,Svelte 就是一個特別好的選擇。下面是Jacek Schae大神的統計,使用市面上主流的框架,來編寫同樣的Realword 應用的體積:

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

從上圖的統計,Svelte簡直是神奇!竟然只有 9。7 KB ! 果然魔法消失 UI 框架,無愧其名。

可以看出,Svelte的bundle size大小是Vue的1/4,是React的1/20,體積上的優勢還是相當明顯的。

5。2 Less-Code ——寫更少的程式碼

在寫svelte元件時,你就會發現,和 Vue 或 React 相比只需要更少的程式碼。開發者的夢想之一,就是敲更少的程式碼。因為更少的程式碼量,往往意味著有更好的語義性,也有更少的機率寫出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設定狀態。而在Svelte中,可以直接使用賦值運算子更新狀態。

如果說上面的例子太簡單了,可以看下面的統計,分別使用 React 和 Svelte 實現下面的元件所需要的程式碼行數

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

下面還是 Jacek Schae 老哥的統計,編寫同樣的Realword 應用,各個框架所需要的行數

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

Vue 和 React 打了平手,Svelte 遙遙領先,可以少些 1000 行程式碼耶!早日下班,指日可待。

5。3 Hight-Performance ——高效能

在Virtual Dom已經是前端框架標配的今天, Svelte 聲稱自己是沒有Virtual Dom加持的, 怎麼還能保證高效能呢?不急,慢慢看。

效能測評

Jacek Schae 在《A RealWorld Comparison of Front-End Frameworks with Benchmarks》中用主流的前端框架來編寫 RealWorld 應用,使用 Chrome 的Lighthouse Audit測試效能,得出資料是

Svelte 略遜於Vue, 但好於 React。

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

是不是很驚奇?另外一個前端框架效能對比的專案也給出了同樣的答案:https://github。com/krausest/js-framework-benchmark。

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

為什麼 Svelte 效能還不錯,至少沒有我們預期的那麼糟糕?我們接下來會在原理那一小結來介紹。

6。Svelte 劣勢

說完了 Svelte 的優勢,我們也要考慮到 Svelte 的劣勢。

和Vue, React框架的對比

在構建大型前端專案時,我們在選擇框架的時候就需要考慮更多的事情。Svelte 目前尚處在起步階段,對於大型專案必要的

單元測試

並沒有完整的方案。目前在大型應用中使用 Svelte , 需要謹慎評。

類目

Svelte

Vue

React

UI 元件庫

Material design ( 坦率的說,不好用 )

Element UI / AntD

AntD / Material design

狀態管理

官網自帶

Vuex

Redux/MobX

路由

Svelte-router

Vue-router

React-router

服務端渲染

支援

支援

支援

測試工具

官方網站沒有相關內容

@vue/test-utils

Jest

我們在用 Svelte 開發公司級別中大型專案時,也發現了其他的一些主要注意的點

沒有像AntD那樣成熟的UI庫。比如說需求方想加一個toast提示,或者彈窗,pm:”很簡單的,不用出UI稿,就直接用之前的樣式好啦~“

但是 Svelte 需要從0開始 ”抄“ 出來一個toast或者彈窗元件出來,可能會帶來額外的開發量和做好加班的準備。

Svelte 原生不支援預處理器,比如說less/scss,需要自己單獨配置webpack loader。

Svelte 原生腳手架沒有目錄劃分

暫時不支援typescript,雖然官方說了會支援, 但是不知道什麼時候。

還需要注意的一點是,React / Vue等框架自帶的

runtime

雖然會增加首屏載入的bundle。js,可是當專案變得越來越大的時候,框架的runtime在bundle。js裡面佔據的比例也會越來越小,這個時候我們就得考慮一下是不是存在一個Svelte生成的程式碼大於React和Vue生成的程式碼的閾值了。

7。原理篇

Svelte 原理相對於 React 和 Vue 來說,相對比較簡單,大家可以放心的往下看。

首先,我們從一個問題出發:

Virtual Dom 真的高效嗎

Rich Harris 在設計 Svelte 的時候沒有采用 Virtual DOM 是因為覺得Virtual DOM Diff 的過程是非常低效的。

在他的一文《Virtual DOM is pure overhead》原文連線:https://www。sveltejs。cn/blog/virtual-dom-is-pure-overhead,感興趣的同學可以翻一下。

人們覺得 Virtual DOM高效的一個理由,就是它不會直接操作原生的DOM節點。

在瀏覽器當中,JavaScript的運算在現代的引擎中非常快,但DOM本身是非常緩慢的東西

。當你呼叫原生DOM API的時候,瀏覽器需要在JavaScript引擎的語境下去接觸原生的DOM的實現,這個過程有相當的效能損耗。

但其實 Virtual DOM 有時候會做很多無用功,這體現在很多元件會被“無緣無故”進行重渲染(re-render)。

比如說,下面的例子中,React 為了更新掉message 對應的DOM 節點,需要做n多次遍歷,才能找到具體要更新哪些節點。

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

為了解決這個問題,React 提供pureComponent,shouldComponentUpdate,useMemo,useCallback讓開發者來操心哪些subtree是需要重新渲染的,哪些是不需要重新渲染的。究其本質,是因為 React 採用 jsx 語法過於靈活,不理解開發者寫出程式碼所代表的意義,沒有辦法做出最佳化。

所以,React 為了解決這個問題,在 v16。0 帶來了全新的 Fiber 架構,Fiber 思路是不減少渲染工作量,把渲染工作拆分成小任務思路是不減少渲染工作量。渲染過程中,留出時間來處理使用者響應,讓使用者感覺起來變快了。這樣會帶來額外的問題,不得不載入額外的程式碼,用於處理複雜的執行時排程工作

那麼 Svelte 是如何解決這個問題的?

React 採用 jsx 語法本質不理解資料代表的意義,沒有辦法做出最佳化。Svelte 採用了Templates語法(類似於 Vue 的寫法),更加嚴格和具有語義性,可以在編譯的過程中就進行最佳化操作。

那麼,為什麼Templates語法可以解決這個問題呢?

Template 帶來的優勢

關於 JSX 與 Templates ,可以看成是兩種不同的前端框架渲染機制,有興趣的同學可以翻一下尤雨溪的演講《在框架設計中尋求平衡》:https://www。bilibili。com/video/av80042358/。

一方面, JSX 的代表框架有 React 以及所有 react-like 庫,比如 preact、 stencil, infernal 等;另一方面, Templates 代表性的解決方案有 Vue、Svelte、 ember,各有優缺點。

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

JSX 優缺點

jsx 具有 JavaScript 的完整表現力,非常具有表現力,可以構建非常複雜的元件。

但是靈活的語法,也意味著引擎難以理解,無法預判開發者的使用者意圖,從而難以最佳化效能。你很可能會寫出下面的程式碼:

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

在使用 JavaScript 的時候,編譯器不可能hold住所有可能發生的事情,因為 JavaScript 太過於動態化。也有人對這塊做了很多嘗試,但從本質上來說很難提供安全的最佳化。

Template優缺點

Template模板是一種非常有約束的語言,你只能以某種方式去編寫模板。

例如,當你寫出這樣的程式碼的時候,編譯器可以立刻明白:

”哦!這些 p 標籤的順序是不會變的,這個 id 是不會變的,這些 class 也不會變的,唯一會變的就是這個“

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

在編譯時,編譯器對你的意圖可以做更多的預判,從而給它更多的空間去做執行最佳化。

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

左側 template 中,其他所有內容都是靜態的,只有 name 可能會發生改變。

右側 p 函式是編譯生成的最終的產物,是原生的js可以直接執行在瀏覽器裡,會在有髒資料時被呼叫。p 函式唯一做的事情就是,當 name 發生變更的時候,呼叫原生方法把 t1 這個原生DOM節點更新。這裡的 set_data 可不是 React 的 setState 或者小程式的 setData ,這裡的set_data 就是封裝的原生的 javascript 操作DOM 節點的方法。

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

如果我們仔細觀察上面的程式碼,發現問題的關鍵在於 if 語句的判斷條件——changed。name, 表示有哪些變數被更新了,這些被更新的變數被稱為髒資料。

任何一個現代前端框架,都需要記住哪些

資料更新

了,根據更新後的資料渲染出最新的DOM

Svelte 記錄髒資料的方式:位掩碼(bitMask)

Svelte使用

位掩碼(bitMask)

的技術來跟蹤哪些值是髒的,即自元件最後一次更新以來,哪些資料發生了哪些更改。

位掩碼是一種將多個布林值儲存在單個整數中的技術,一個位元位存放一個數據是否變化,一般1表示髒資料,0表示是乾淨資料。

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

用大白話來講,你有A、B、C、D 四個值,那麼二進位制0000 0001表示第一個值A發生了改變,0000 0010表示第二個值B發生了改變,0000 0100表示第三個值C發生了改變,0000 1000表示第四個D發生了改變。

這種表示法,可以最大程度的利用空間。為啥這麼說呢?比如說,十進位制數字3就可以表示 A、B是髒資料。先把十進位制數字3, 轉變為二進位制0000 0011。從左邊數第一位、第二位是1,意味著第一個值A 和第二個值B是髒資料;其餘位都是0,意味著其餘資料都是乾淨的。

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

JS 的限制

那麼,是不是用二進位制位元位就可以記錄各種無窮無盡的變化了呢?

JS 的二進位制有31位限制,number 型別最長是32位,減去1位用來存放符號。也就是說,如果 Svelte 採用二進位制位儲存的方法,那麼只能存 31個數據。

但肯定不能這樣,對吧?

Svelte 採用陣列來存放

,陣列中一項是二進位制31位的位元位。假如超出31個數據了,超出的部分放到陣列中的下一項。

這個陣列就是component.$.dirty陣列,二進位制的1位表示該對應的資料發生了變化,是髒資料,需要更新;二進位制的0位表示該對應的資料沒有發生變化,是乾淨的

一探究竟component。$。dirty

上文中,我們說到component。$。dirty是陣列,具體這個陣列長什麼樣呢?

我們模擬一個 Svelte 元件,這個 Svelte 元件會修改33個數據。

我們打印出每一次make_dirty之後的component.$.dirty, 為了方便演示,轉化為二進位制打印出來,如下面所示:

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

上面陣列中的每一項中的每一個位元位,如果是1,則代表著該資料是否是髒資料。如果是髒資料,則意味著更新。

第一行[“0000000000000000000000000000001”, “0000000000000000000000000000000”], 表示第一個資料髒了,需要更新第一個資料對應的dom節點

第二行[“0000000000000000000000000000011”, “0000000000000000000000000000000”], 表示第一個、第二個資料都髒了,需要更新第一個,第二個資料對應的dom節點。

……

當一個元件內,資料的個數,超出了31的數量限制,就陣列新增一項來表示。

這樣,我們就可以透過component。$。dirty這個陣列,清楚的知道有哪些資料發生了變化。那麼具體應該更新哪些DOM 節點呢?

資料和DOM節點之間的對應關係

我們都知道, React 和 Vue 是透過 Virtual Dom 進行 diff 來算出來更新哪些 DOM 節點效率最高。Svelte 是在編譯時候,就記錄了資料 和 DOM 節點之間的對應關係,並且儲存在 p 函式中。

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

這裡說的p 函式,就是 Svelte 的更新方法,本質上就是一大堆if判斷,邏輯非常簡單

if ( A 資料變了 ) { 更新A對應的DOM節點}if ( B 資料變了 ) { 更新B對應的DOM節點}

為了更加直觀的理解,我們模擬更新一下33個數據的元件,編譯得到的p 函式打印出來,如:

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

我們會發現,裡面就是一大堆if判斷,但是if判斷條件比較有意思,我們從上面摘取一行仔細觀察一下:

「轉」新興前端框架 Svelte?能否超越React/Vue/Angular?

首先要注意,&不是邏輯與,而是按位與,會把兩邊數值轉為二進位制後進行比較,只有相同的二進位制位都為1 才會為真。

這裡的if判斷條件是:拿compoenent.$.dirty[0](00000000000000000000000000000100)和4(4 轉變為二進位制是0000 0100)做按位並操作。那麼我們可以思考一下了,這個按位並操作什麼時候會返回1呢?

4是一個常量,轉變為二進位制是0000 0100, 第三位是1。那麼也就是,只有dirty[0]的二進位制的第三位也是1時, 表示式才會返回真。換句話來說,只有第三個資料是髒資料,才會走入到這個if判斷中,執行set_data(t5, ctx[2]), 更新t5這個 DOM 節點。

當我們分析到這裡,已經看出了一些眉目,讓我們站在更高的一個層次去看待這 30多行程式碼:

它們其實是儲存了這33個變數 和 真實DOM 節點之間的對應關係,哪些變數髒了,Svelte 會走入不同的if體內直接更新對應的DOM節點,而不需要複雜 Virtual DOM DIFF 算出更新哪些DOM節點

這 30多行程式碼,是Svelte 編譯了我們寫的Svelte 元件之後的產物,在Svelte 編譯時,就已經分析好了,資料 和 DOM 節點之間的對應關係,在資料發生變化時,可以非常高效的來更新DOM節點。

Vue 曾經也是想採取這樣的思路,但是 Vue 覺得儲存每一個髒資料太消耗記憶體了,於是沒有采用那麼細顆粒度,而是以元件級別的中等顆粒度,只監聽到元件的資料更新,元件內部再透過 DIFF 演算法計算出更新哪些 DOM 節點。Svelte 採用了位元位的儲存方式,解決了儲存髒資料會消耗記憶體的問題。

整體流程

上面就是Svelte 最核心更新DOM機制,下面我們串起來整個的流程。

下面是非常簡單的一個 Svelte 元件,點選