React Fiber很難? 六個問題助你理解 React Fiber

blank

React Fiber很難? 六個問題助你理解 React Fiber

React Fiber 是Facebook花費兩年餘時間對 React 做出的一個重大改變與優化,是對 React 核心演算法的一次重新實現。 從Facebook在 React Conf 2017會議上確認,React Fiber 會在React 16 版本發佈至今,也已過去三年有餘,如今,React 17 業已發佈,社區關於Fiber的優秀文章不在少數。

本文源於一次團隊內部的技術分享,借鑒社區優秀文章,結合個人理解,進行整合,從六個問題出發,對 React Fiber 進行理解與認識,同時對時下熱門的前端框架Svelte進行簡要介紹與剖析,希望對正在探究 React 及各前端框架的小夥伴們能有所助益。

全文大量參考和引用以下幾篇博文,讀者可自行查閱:

一、React 的設計理念是什麼?

React官網在React哲學一節開篇提到:

我們認為,React 是用 JavaScript 構建快速回應的大型 Web 應用程式的首選方式。 它在Facebook和Instagram上表現優秀。 React 最棒的部分之一是引導我們思考如何構建一個應用。

由此可見,React 追求的是 「快速回應」,那麼,「快速回應」的制約因素都有什麼呢?

  • CPU的瓶頸:當專案變得龐大、元件數量繁多、遇到大計算量的操作或者設備性能不足使得頁面掉幀,導致卡頓。
  • IO的瓶頸:發送網路請求后,由於需要等待數據返回才能進一步操作導致不能快速回應。

本文要聊的fiber 架構主要就是用來解決CPU和網路的問題,這兩個問題一直也是最影響前端開發體驗的地方,一個會造成卡頓,一個會造成白屏。 此 react 為前端引入了兩個新概念:Time Slicing 時間分片和Suspense

二、React的「先天不足」 —— 聽說 Vue 3.0 採用了動靜結合的 Dom diff,React 為何不跟進?

Vue 3.0 動靜結合的 Dom diff

Vue3.0 提出動靜結合的 DOM diff 思想,動靜結合的 DOM diff其實是在預編譯階段進行了優化。 之所以能夠做到預編譯優化,是因為 Vue core 可以靜態分析 template,在解析模版時,整個 parse 的過程是利用正則表達式順序解析範本,當解析到開始標籤、閉合標籤和文本的時候都會分別執行對應的回調函數,來達到構造 AST 樹的目的。

借助預編譯過程,Vue 可以做到的預編譯優化就很強大了。 比如在預編譯時標記出模版中可能變化的元件節點,再次進行渲染前diff時就可以跳過"永遠不會變化的節點",而只需要對比"可能會變化的動態節點"。 這也就是動靜結合的 DOM diff 將 diff 成本與模版大小正相關優化到與動態節點正相關的理論依據

React 能否像 Vue 那樣進行預編譯優化?

Vue 需要做數據雙向綁定,需要進行數據攔截或代理,那它就需要在預編譯階段靜態分析模版,分析出視圖依賴了哪些數據,進行回應式處理。 而 React 就是局部重新渲染,React 拿到的或者說掌管的,所負責的就是一堆遞歸 React.createElement 的執行調用(參考下方經過Babel轉換的代碼),它無法從模版層面進行靜態分析。 JSX 和手寫的 render function 是完全動態的,過度的靈活性導致運行時可以用於優化的訊息不足

JSX 寫法:

<div><h1>六个问题助你理解 React Fiber</h1><ul><li>React</li><li>Vue</li></ul></div>

遞歸 React.createElement:

// Babel转换后
React.createElement("div",null,React.createElement("h1",null,"u516Du4E2Au95EEu9898u52A9u4F60u7406u89E3 React Fiber"),React.createElement("ul",null,React.createElement("li",null,"React"),React.createElement("li",null,"Vue")));

JSX vs Template

blank
  • JSX 具有 JavaScript 的完整表現力,可以構建非常複雜的元件。 但是靈活的語法,也意味著引擎難以理解,無法預判開發者的使用者意圖,從而難以優化性能。
  • Template 範本是一種非常有約束的語言,你只能以某種方式去編寫範本。

既然存在以上編譯時先天不足,在運行時優化方面,React一直在努力。 比如,React15實現了batchedUpdates(批量更新)。 即同一事件回調函數上下文中的多次setState只會觸發一次更新。

但是,如果單次更新就很耗時,頁面還是會卡頓(這在一個維護時間很長的大應用中是很常見的)。 這是因為React15的更新流程是同步執行的,一旦開始更新直到頁面渲染前都不能中斷。

資料參考:以 React 為例,說說框架和性能(下) | 新興前端框架 Svelte 從入門到原理

三、從架構演變看不斷進擊的 React 都做過哪些優化?

React渲染頁面的兩個階段

  • 調度階段(reconciliation):在這個階段 React 會更新數據生成新的 Virtual DOM,然後通過Diff演算法,快速找出需要更新的元素,放到更新佇列中去,得到新的更新佇列
  • 渲染階段(commit):這個階段 React 會遍曆更新佇列,將其所有的變更一次性更新到DOM上

React 15 架構

React15架構可以分為兩層:

  • Reconciler(協調器)—— 負責找出變化的元件;
  • Renderer(渲染器)—— 負責將變化的元件渲染到頁面上;

在React15及以前,Reconciler採用遞歸的方式創建虛擬DOM,遞歸過程是不能中斷的。 如果元件樹的層級很深,遞歸會佔用線程很多時間,遞歸更新時間超過了16ms,使用者交互就會卡頓。

為了解決這個問題,React16將遞歸的無法中斷的更新重構為異步的可中斷更新,由於曾經用於遞歸的虛擬DOM數據結構已經無法滿足需要。 於是,全新的Fiber架構應運而生。

React 16 架構

為了解決同步更新長時間佔用線程導致頁面卡頓的問題,也為了探索運行時優化的更多可能,React開始重構並一直持續至今。 重構的目標是實現Concurrent Mode(併發模式)。

從v15到v16,React團隊花了兩年時間將源碼架構中的Stack Reconciler重構為Fiber Reconciler。

React16架構可以分為三層:

  • Scheduler(調度器)—— 調度任務的優先順序,高優任務優先進入Reconciler;
  • Reconciler(協調器)—— 負責找出變化的元件:更新工作從遞歸變成了可以中斷的循環過程。 Reconciler內部採用了Fiber的架構;
  • Renderer(渲染器)—— 負責將變化的元件渲染到頁面上。

React 17 優化

React16的expirationTimes模型只能區分是否 >=expirationTimes 決定節點是否更新。 React17的lanes模型可以選定一個更新區間,並且動態的向區間中增減優先順序,可以處理更細粒度的更新。

Lane用二進位位表示任務的優先順序,方便優先順序的計算(位運算),不同優先順序佔用不同位置的"賽道",而且存在批的概念,優先順序越低,"賽道"越多。 高優先順序打斷低優先順序,新建的任務需要賦予什麼優先順序等問題都是Lane所要解決的問題。

Concurrent Mode的目的是實現一套可中斷/恢復的更新機制。 其由兩部分組成:

  • 一套協程架構:Fiber Reconciler
  • 基於協程架構的啟發式更新演算法:控制協程架構工作方式的演算法

資料參考:React17 新特性:啟發式更新演算法

四、瀏覽器一幀都會幹些什麼以及requestIdleCallback的啟示

瀏覽器一幀都會幹些什麼?

我們都知道,頁面的內容都是一幀一幀繪製出來的,瀏覽器刷新率代表瀏覽器一秒繪製多少幀。 原則上說 1s 內繪製的幀數也多,畫面表現就也細膩。 目前瀏覽器大多是 60Hz(60幀/s),每一幀耗時也就是在 16.6ms 左右。 那麼在這一幀的(16.6ms) 過程中瀏覽器又幹了些什麼呢?

blank

通過上面這張圖可以清楚的知道,瀏覽器一幀會經過下面這幾個過程:

  1. 接受輸入事件
  2. 執行事件回調
  3. 開始一幀
  4. 執行 RAF (RequestAnimationFrame)
  5. 頁面布局,樣式計算
  6. 繪製渲染
  7. 執行 RIC (RequestIdelCallback)

第七步的 RIC 事件不是每一幀結束都會執行,只有在一幀的 16.6ms 中做完了前面 6 件事兒且還有剩餘時間,才會執行。 如果一幀執行結束後還有時間執行 RIC 事件,那麼下一幀需要在事件執行結束才能繼續渲染,所以 RIC 執行不要超過 30ms,如果長時間不將控制權交還給瀏覽器,會影響下一幀的渲染,導致頁面出現卡頓和事件回應不及時。

requestIdleCallback 的啟示

我們以瀏覽器是否有剩餘時間作為任務中斷的標準,那麼我們需要一種機制,當瀏覽器有剩餘時間時通知我們。

requestIdleCallback((deadline)=>{// deadline 有两个参数
// timeRemaining(): 当前帧还剩下多少时间
// didTimeout: 是否超时
// 另外 requestIdleCallback 后如果跟上第二个参数 {timeout: ...} 则会强制浏览器在当前帧执行完后执行。
if(deadline.timeRemaining()>0){// TODO
}else{requestIdleCallback(otherTasks);}});// 用法範例
vartasksNum=10000requestIdleCallback(unImportWork)functionunImportWork(deadline){while(deadline.timeRemaining()&&tasksNum>0){console.log(执行了</span><span class="si">${</span><span class="mi">10000</span><span class="o">-</span><span class="nx">tasksNum</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="sb">个任务)tasksNum--}if(tasksNum>0){// 在未来的帧中继续执行
requestIdleCallback(unImportWork)}}

其實部分瀏覽器已經實現了這個API,這就是requestIdleCallback。 但是由於以下因素,Facebook 拋棄了 requestIdleCallback 的原生 API:

  • 瀏覽器相容性;
  • 觸發頻率不穩定,受很多因素影響。 比如當我們的瀏覽器切換tab后,之前tab註冊的requestIdleCallback觸發的頻率會變得很低。

參考: requestIdleCallback 的 FPS 只有 20

基於以上原因,在React中實現了功能更完備的requestIdleCallbackpolyfill,這就是Scheduler。 除了在空閒時觸發回調的功能外,Scheduler還提供了多種調度優先順序供任務設置。

資料參考:requestIdleCallback-後台工作調度

五、 Fiber 為什麼是 React 性能的一個飛躍?

什麼是 Fiber

Fiber 的英文含義是「纖維」,它是比線程(Thread)更細的線,比線程(Thread)控制得更精密的執行模型。 在廣義計算機科學概念中,Fiber 又是一種協作的(Cooperative)程式設計模型(協程),幫助開發者用一種【既模組化又協作化】的方式來編排代碼。

在 React 中,Fiber 就是 React 16 實現的一套新的更新機制,讓 React 的更新過程變得可控,避免了之前採用遞歸需要一氣呵成影響性能的做法

React Fiber 中的時間分片

把一個耗時長的任務分成很多小片,每一個小片的運行時間很短,雖然總時間依然很長,但是在每個小片執行完之後,都給其他任務一個執行的機會,這樣唯一的線程就不會被獨佔,其他任務依然有運行的機會。

React Fiber 把更新過程碎片化,每執行完一段更新過程,就把控制權交還給 React 負責任務協調的模組,看看有沒有其他緊急任務要做,如果沒有就繼續去更新,如果有緊急任務,那就去做緊急任務。

Stack Reconciler

基於棧的 Reconciler,瀏覽器引擎會從執行棧的頂端開始執行,執行完畢就彈出當前執行上下文,開始執行下一個函數,直到執行棧被清空才會停止。 然後將執行權交還給瀏覽器。 由於 React 將頁面視作一個個函數執行的結果。 每一個頁面往往由多個視圖組成,這就意味著多個函數的調用。

如果一個頁面足夠複雜,形成的函數調用棧就會很深。 每一次更新,執行棧需要一次性執行完成,中途不能干其他的事兒,只能"一心一意"。 結合前面提到的瀏覽器刷新率,JS 一直執行,瀏覽器得不到控制權,就不能及時開始下一幀的繪製。 如果這個時間超過 16ms,當頁面有動畫效果需求時,動畫因為瀏覽器不能及時繪製下一幀,這時動畫就會出現卡頓。 不僅如此,因為事件響應代碼是在每一幀開始的時候執行,如果不能及時繪製下一幀,事件回應也會延遲。

Fiber Reconciler

鏈表結構

在 React Fiber 中用鏈表遍曆的方式替代了 React 16 之前的棧遞歸方案。 在 React 16 中使用了大量的鏈表。

  • 使用多向鏈錶的形式替代了原來的樹結構;
<divid="A">
  A1
  <divid="B1">
    B1
    <divid="C1"></div></div><divid="B2">
    B2
  </div></div>
blank
  • 副作用單鏈表;
blank
  • 狀態更新單鏈表;
blank
  • ...

鏈錶是一種簡單高效的數據結構,它在當前節點中保存著指向下一個節點的指標;遍歷的時候,通過操作指標找到下一個元素。

blank

鏈表相比順序結構數據格式的好處就是:

  1. 操作更高效,比如順序調整、刪除,只需要改變節點的指標指向就好了。
  2. 不僅可以根據當前節點找到下一個節點,在多向鏈表中,還可以找到他的父節點或者兄弟節點。

但鏈表也不是完美的,缺點就是:

  1. 比順序結構數據更佔用空間,因為每個節點物件還保存有指向下一個物件的指標。
  2. 不能自由讀取,必須找到他的上一個節點。

React 用空間換時間,更高效的操作可以方便根據優先順序進行操作。 同時可以根據目前的節點找到其他節點,在下面提到的掛起和恢復過程中起到了關鍵作用

斐波那契數列的 Fiber

遞歸形式的斐波那契數列寫法:

functionfib(n){if(n<=2){return1;}else{returnfib(n-1)+fib(n-2);}}

採用 Fiber 的思路將其改寫為迴圈(這個例子並不能和 React Fiber 的對等):

functionfib(n){letfiber={arg:n,returnAddr:null,a:0},consoled=false;// 标记循环
rec:while(true){// 当展开完全后,开始计算
if(fiber.arg<=2){letsum=1;// 寻找父级
while(fiber.returnAddr){if(!consoled){// 在这里打印查看形成的链表形式的 fiber 对象
consoled=trueconsole.log(fiber)}fiber=fiber.returnAddr;if(fiber.a===0){fiber.a=sum;fiber={arg:fiber.arg-2,returnAddr:fiber,a:0};continuerec;}sum+=fiber.a;}returnsum;}else{// 先展开
fiber={arg:fiber.arg-1,returnAddr:fiber,a:0};}}}

六、React Fiber 是如何實現更新過程可控?

更新過程的可控主要體現在下面幾個方面:

  • 任務拆分
  • 任務掛起、恢復、終止
  • 任務具備優先順序

任務拆分

在 React Fiber 機制中,它採用「化整為零」的思想,將調和階段(Reconciler)遞歸遍歷 VDOM 這個大任務分成若干小任務,每個任務只負責一個節點的處理。

任務掛起、恢復、終止

workInProgress tree

workInProgress 代表目前正在執行更新的 Fiber 樹。 在 render 或者 setState 後,會構建一顆 Fiber 樹,也就是 workInProgress tree,這棵樹在構建每一個節點的時候會收集當前節點的副作用,整棵樹構建完成後,會形成一條完整的副作用鏈

currentFiber tree

currentFiber 表示上次渲染構建的 Filber 樹在每一次更新完成後 workInProgress 會賦值給 currentFiber。 在新一輪更新時 workInProgress tree 再重新構建,新 workInProgress 的節點通過 alternate 屬性和 currentFiber 的節點建立聯繫。

在新 workInProgress tree 的創建過程中,會同 currentFiber 的對應節點進行 Diff 比較,收集副作用。 同時也會複用和 currentFiber 對應的節點對象,減少新創建對象帶來的開銷。 也就是說無論是建立還是更新、掛起、恢復以及終止操作都是發生在 workInProgress tree 建立過程中的。 workInProgress tree 構建過程其實就是循環的執行任務和創建下一個任務。

掛起

當第一個小任務完成後,先判斷這一幀是否還有空閒時間,沒有就掛起下一個任務的執行,記住當前掛起的節點,讓出控制權給瀏覽器執行更高優先順序的任務。

恢復

在瀏覽器渲染完一幀后,判斷當前幀是否有剩餘時間,如果有就恢復執行之前掛起的任務。 如果沒有任務需要處理,代表調和階段完成,可以開始進入渲染階段。

  1. 如何判斷一幀是否有空閒時間的呢?

使用前面提到的 RIC (RequestIdleCallback) 瀏覽器原生 API,React 源碼中為了相容低版本的瀏覽器,對該方法進行了 Polyfill。

  1. 恢復執行的時候又是如何知道下一個任務是什麼呢?

答案是在前面提到的鏈表。 在 React Fiber 中每個任務其實就是在處理一個 FiberNode 對象,然後又生成下一個任務需要處理的 FiberNode。

終止

其實並不是每次更新都會走到提交階段。 當在調和過程中觸發了新的更新,在執行下一個任務的時候,判斷是否有優先順序更高的執行任務,如果有就終止原來將要執行的任務,開始新的 workInProgressFiber 樹構建過程,開始新的更新流程。 這樣可以避免重複更新操作。 這也是在 React 16 以後生命週期函數 componentWillMount 有可能會執行多次的原因。

blank

任務具備優先順序

React Fiber 除了通過掛起,恢復和終止來控制更新外,還給每個任務分配了優先順序。 具體點就是在創建或者更新 FiberNode 的時候,通過演算法給每個任務分配一個到期時間(expirationTime)。 在每個任務執行的時候除了判斷剩餘時間,如果當前處理節點已經過期,那麼無論現在是否有空閒時間都必須執行該任務。 過期時間的大小還代表著工作的優先順序

任務在執行過程中順便收集了每個 FiberNode 的副作用,將有副作用的節點通過 firstEffect、lastEffect、nextEffect 形成一條副作用單鏈表 A1(TEXT)-B1(TEXT)-C1(TEXT)-C1-C2(TEXT)-C2-B1-B2(TEXT)-B2-A。

其實最終都是為了收集到這條副作用鏈錶,有了它,在接下來的渲染階段就通過遍歷副作用鏈完成 DOM 更新。 這裡需要注意,更新真實 DOM 的這個動作是一氣呵成的,不能中斷,不然會造成視覺上的不連貫(commit)。

<divid="A1">
  A1
  <divid="B1">
    B1
    <divid="C1">C1</div><divid="C2">C2</div></div><divid="B2">
    B2
  </div></div>
blank

直觀展示

正是基於以上這些過程,使用Fiber,我們就有了在社區經常看到的兩張對比圖

blank
blank

清晰展示及交互、原始碼可通過下面兩個鏈接進入,查看網頁原始程式碼。

Fiber 結構長什麼樣?

基於時間分片的增量更新需要更多的上下文訊息,之前的vDOM tree顯然難以滿足,所以擴展出了fiber tree(即Fiber上下文的vDOM tree),更新過程就是根據輸入數據以及現有的fiber tree構造出新的fiber tree(workInProgress tree)。

FiberNode 上的屬性有很多,根據筆者的理解,以下這麼幾個屬性是值得關注的:return、child、sibling(主要負責fiber鏈表的連結);stateNode;effectTag;expirationTime;alternate;nextEffect。 各屬性介紹參看下面的 class FiberNode

classFiberNode{constructor(tag,pendingProps,key,mode){實例屬性this.tag=tag;標記不同元件類型,如函數位件、類元件、文字、原生元件...this.key=key;react 元素上的key就是 jsx上寫的那個key,也就是最終ReactElement上的this.elementType=null;createElement的第一個參數,ReactElement 上的 typethis.type=null;表示fiber的真實類型 ,elementType 基本一樣,在使用了懶載入之類的功能時可能會不一樣this.stateNode=null;實例物件,比如 class 元件 new 完後就掛載在這個屬性上面,如果是RootFiber,那麼它上面掛的是 FiberRoot,如果是原生節點就是 dom 物件fiberthis.return=null;父節點,指向上一個 fiberthis.child=null;子節點,指向自身下面的第一個 fiberthis.sibling=null;兄弟元件, 指向一個兄弟節點this.index=0;一般如果沒有兄弟節點的話是0 當某個父節點下的子節點是數位類型的時候會給每個子節點一個 index,index 和 key 要一起做 diffthis.ref=null;reactElement 上的 ref 屬性this.pendingProps=pendingProps;新的 propsthis.memoizedProps=null;舊的 propsthis.updateQueue=null;fiber 上的更新佇列執行一次 setState 就會往這個屬性上掛一個新的更新, 每條更新最終會形成一個鏈表結構,最後做批量更新this.memoizedState=null;對應 memoizedProps,上次渲染的 state,相當於當前的 state,理解成 prev 和 next 的關係this.mode=mode;表示當前元件下的子元件的渲染方式effectsthis.effectTag=NoEffect;表示目前 fiber 要進行何種更新(更新、刪除等)this.nextEffect=null;指向下個需要更新的fiberthis.firstEffect=null;指向所有子節點里,需要更新的 fiber 裡的第一個this.lastEffect=null;指向所有子節點中需要更新的 fiber 的最後一個this.expirationTime=NoWork;過期時間,代表任務在未來的哪個時間點應該被完成this.childExpirationTime=NoWork;child 過期時間this.alternate=null;current 樹和 workInprogress 樹之間的相互引用}}

blank

圖片來源:完全理解React Fiber

functionperformUnitWork(currentFiber){//beginWork(currentFiber) //找到儿子,并通过链表的方式挂到currentFiber上,每一偶儿子就找后面那个兄弟
//有儿子就返回儿子
if(currentFiber.child){returncurrentFiber.child;}//如果没有儿子,则找弟弟
while(currentFiber){//一直往上找
//completeUnitWork(currentFiber);//将自己的副作用挂到父节点去
if(currentFiber.sibling){returncurrentFiber.sibling}currentFiber=currentFiber.return;}}

Concurrent Mode (併發模式)

Concurrent Mode 指的就是 React 利用上面 Fiber 帶來的新特性的開啟的新模式 (mode)。 react17開始支援concurrent mode,這種模式的根本目的是為了讓應用保持cpu和io的快速回應,它是一組新功能,包括Fiber、Scheduler、Lane,可以根據用戶硬體性能和網路狀況調整應用的回應速度,核心就是為了實現異步可中斷的更新。 concurrent mode也是未來react主要反覆運算的方向。

目前 React 實驗版本允許使用者選擇三種 mode:

  1. Legacy Mode: 就相當於目前穩定版的模式
  2. Blocking Mode: 應該是以後會代替 Legacy Mode 而長期存在的模式
  3. Concurrent Mode: 以後會變成 default 的模式

Concurrent Mode 其實開啟了一堆新特性,其中有兩個最重要的特性可以用來解決我們開頭提到的兩個問題:

  1. Suspense:Suspense 是 React 提供的一種異步處理的機制, 它不是一個具體的數據請求庫。 它是React提供的原生的元件異步調用原語。
  2. useTrasition:讓頁面實現 Pending -> Skeleton -> Complete 的更新路徑, 使用者在切換頁面時可以停留在當前頁面,讓頁面保持回應。 相比展示一個無用的空白頁面或者載入狀態,這種用戶體驗更加友好。

其中 Suspense 可以用來解決請求阻塞的問題,UI 卡頓的問題其實開啟 concurrent mode 就已經解決的,但如何利用 concurrent mode 來實現更友好的交互還是需要對代碼做一番改動的。

資料參考:Concurrent 模式介紹 (實驗性) | 理解 React Fiber & Concurrent Mode | 11.concurrent mode(併發模式是什麼樣的) | 人人都能讀懂的react源碼解析

未來可期

Concurrent Mode只是併發,既然任務可拆分(只要最終得到完整effect list就行),那就允許並行執行,(多個Fiber reconciler + 多個worker),首屏也更容易分塊載入/渲染(vDOM森林。

並行渲染的話,據說Firefox測試結果顯示,130ms的頁面,只需要30ms就能搞定,所以在這方面是值得期待的,而React已經做好準備了,這也就是在React Fiber上下文經常聽到的待unlock的更多特性之一。

isInputPending —— Fiber架構思想對前端生態的影響

Facebook 在 Chromium 中提出並實現了 isInputPending() API ,它可以提高網頁的回應能力,但是不會對性能造成太大影響。 Facebook 提出的 isInputPending API 是第一個將中斷的概念用於瀏覽器使用者交互的的功能,並且允許 JavaScript 能夠檢查事件佇列而不會將控制權交於瀏覽器。

目前 isInputPending API 僅在 Chromium 的 87 版本開始提供,其他瀏覽器並未實現。

blank

資料參考:Facebook 將對 React 的優化實現到了瀏覽器! | Faster input events with Facebook’s first browser API contribution

Svelte 對固有模式的衝擊

當下前端領域,三大框架React、Vue、Angular版本逐漸穩定,如果說前端行業會出現哪些框架有可能會挑戰React或者Vue呢? 很多人認為Svelte 應該是其中的選項之一。

Svelte叫法是 [Svelte] , 本意是苗條纖瘦的,是一個新興熱門的前端框架。 在開發者滿意度、興趣度、市場佔有率上均名列前茅,同時,它有更小的打包體積,更少的開發代碼書寫,在性能測評中,與React、Vue相比,也不遑多讓。

Svelte 的核心思想在於『透過靜態編譯減少框架運行時的代碼量』。

Svelte 優勢有哪些

  • No Runtime —— 無運行時代碼
  • Less-Code —— 寫更少的代碼
  • Hight-Performance —— 高性能

Svelte 劣勢

  • 社區
  • 社區
  • 社區

原理概覽

Svelte 在編譯時,就已經分析好了數據 和 DOM 節點之間的對應關係,在數據發生變化時,可以非常高效的來更新DOM節點。

  • Rich Harris 在進行Svelte的設計的時候沒有採用 Virtual DOM,主要是因為他覺得Virtual DOM Diff 的過程是非常低效的。 具體可參考Virtual Dom 真的高效嗎一文;Svelte 採用了Templates語法,在編譯的過程中就進行優化操作;
  • Svelte 記錄髒數據的方式:位掩碼(bitMask);
  • 數據和DOM節點之間的對應關係:React 和 Vue 是通過 Virtual Dom 進行 diff 來算出來更新哪些 DOM 節點效率最高。 Svelte 是在編譯時候,就記錄了數據 和 DOM 節點之間的對應關係,並且保存在 p 函數中。
blank

資料參考:新興前端框架 Svelte 從入門到原理

資料參考與推薦

本文發佈於

歡迎指正和star

What do you think?

Written by marketer

blank

太棒了! FaceBook 開源全網第一個時序王器 Kats !

blank

火了! 推薦 10 個低代碼 GitHub 專案