React 性能優(yōu)化之 Fiber

轉(zhuǎn)載自 前端工程師的自我修養(yǎng):React Fiber 是如何實(shí)現(xiàn)更新過(guò)程可控的

前言

從 React 16 開(kāi)始秸苗,React 采用了 Fiber 機(jī)制替代了原先基于原生執(zhí)行棧遞歸遍歷 VDOM 的方案,提高了頁(yè)面渲染性能和用戶(hù)體驗(yàn)谈火。乍一聽(tīng) Fiber 好像挺神秘,在原生執(zhí)行棧都還沒(méi)搞懂的情況下对雪,又整出個(gè) Fiber帜乞,還能不能愉快的寫(xiě)代碼了。別慌余境,老鐵庶骄!下面就來(lái)嘮嘮關(guān)于 Fiber 那點(diǎn)事兒毁渗。

什么是 Fiber

Fiber 的英文含義是“纖維”,它是比線程(Thread)更細(xì)的線单刁,比線程(Thread)控制得更精密的執(zhí)行模型灸异。在廣義計(jì)算機(jī)科學(xué)概念中,F(xiàn)iber 又是一種協(xié)作的(Cooperative)編程模型,幫助開(kāi)發(fā)者用一種【既模塊化又協(xié)作化】的方式來(lái)編排代碼肺樟。

簡(jiǎn)單點(diǎn)說(shuō)檐春,F(xiàn)iber 就是 React 16 實(shí)現(xiàn)的一套新的更新機(jī)制,讓 React 的更新過(guò)程變得可控么伯,避免了之前一竿子遞歸到底影響性能的做法疟暖。

關(guān)于 Fiber 你需要知道的基礎(chǔ)知識(shí)

1 瀏覽器刷新率(幀)

頁(yè)面的內(nèi)容都是一幀一幀繪制出來(lái)的,瀏覽器刷新率代表瀏覽器一秒繪制多少幀田柔。目前瀏覽器大多是 60Hz(60幀/s)俐巴,每一幀耗時(shí)也就是在 16ms 左右。原則上說(shuō) 1s 內(nèi)繪制的幀數(shù)也多硬爆,畫(huà)面表現(xiàn)就也細(xì)膩欣舵。那么在這一幀的(16ms) 過(guò)程中瀏覽器又干了啥呢?

通過(guò)上面這張圖可以清楚的知道缀磕,瀏覽器一幀會(huì)經(jīng)過(guò)下面這幾個(gè)過(guò)程:

  1. 接受輸入事件
  2. 執(zhí)行事件回調(diào)
  3. 開(kāi)始一幀
  4. 執(zhí)行 RAF (RequestAnimationFrame)
  5. 頁(yè)面布局邻遏,樣式計(jì)算
  6. 渲染
  7. 執(zhí)行 RIC (RequestIdelCallback)

第七步的 RIC 事件不是每一幀結(jié)束都會(huì)執(zhí)行,只有在一幀的 16ms 中做完了前面 6 件事兒且還有剩余時(shí)間虐骑,才會(huì)執(zhí)行。這里提一下赎线,如果一幀執(zhí)行結(jié)束后還有時(shí)間執(zhí)行 RIC 事件廷没,那么下一幀需要在事件執(zhí)行結(jié)束才能繼續(xù)渲染,所以 RIC 執(zhí)行不要超過(guò) 30ms垂寥,如果長(zhǎng)時(shí)間不將控制權(quán)交還給瀏覽器颠黎,會(huì)影響下一幀的渲染,導(dǎo)致頁(yè)面出現(xiàn)卡頓和事件響應(yīng)不及時(shí)滞项。

2. JS 原生執(zhí)行棧

React Fiber 出現(xiàn)之前狭归,React 通過(guò)原生執(zhí)行棧遞歸遍歷 VDOM。當(dāng)瀏覽器引擎第一次遇到 JS 代碼時(shí)文判,會(huì)產(chǎn)生一個(gè)全局執(zhí)行上下文并將其壓入執(zhí)行棧过椎,接下來(lái)每遇到一個(gè)函數(shù)調(diào)用,又會(huì)往棧中壓入一個(gè)新的上下文戏仓。比如:

function A(){
  B();
  C();
}
function B(){}
function C(){}
A();

引擎在執(zhí)行的時(shí)候疚宇,會(huì)形成如下這樣的執(zhí)行棧:

瀏覽器引擎會(huì)從執(zhí)行棧的頂端開(kāi)始執(zhí)行,執(zhí)行完畢就彈出當(dāng)前執(zhí)行上下文赏殃,開(kāi)始執(zhí)行下一個(gè)函數(shù)敷待,直到執(zhí)行棧被清空才會(huì)停止。然后將執(zhí)行權(quán)交還給瀏覽器仁热。由于 React 將頁(yè)面視圖視作一個(gè)個(gè)函數(shù)執(zhí)行的結(jié)果榜揖。每一個(gè)頁(yè)面往往由多個(gè)視圖組成,這就意味著多個(gè)函數(shù)的調(diào)用。

如果一個(gè)頁(yè)面足夠復(fù)雜举哟,形成的函數(shù)調(diào)用棧就會(huì)很深思劳。每一次更新,執(zhí)行棧需要一次性執(zhí)行完成炎滞,中途不能干其他的事兒敢艰,只能"一心一意"。結(jié)合前面提到的瀏覽器刷新率册赛,JS 一直執(zhí)行钠导,瀏覽器得不到控制權(quán),就不能及時(shí)開(kāi)始下一幀的繪制森瘪。如果這個(gè)時(shí)間超過(guò) 16ms牡属,當(dāng)頁(yè)面有動(dòng)畫(huà)效果需求時(shí),動(dòng)畫(huà)因?yàn)闉g覽器不能及時(shí)繪制下一幀扼睬,這時(shí)動(dòng)畫(huà)就會(huì)出現(xiàn)卡頓逮栅。不僅如此,因?yàn)槭录憫?yīng)代碼是在每一幀開(kāi)始的時(shí)候執(zhí)行窗宇,如果不能及時(shí)繪制下一幀措伐,事件響應(yīng)也會(huì)延遲。

3. 時(shí)間分片(Time Slicing)

時(shí)間分片指的是一種將多個(gè)粒度小的任務(wù)放入一個(gè)時(shí)間切片(一幀)中執(zhí)行的一種方案军俊,在 React Fiber 中就是將多個(gè)任務(wù)放在了一個(gè)時(shí)間片中去執(zhí)行侥加。

4. 鏈表

在 React Fiber 中用鏈表遍歷的方式替代了 React 16 之前的棧遞歸方案。在 React 16 中使用了大量的鏈表粪躬。例如:

  • 使用多向鏈表的形式替代了原來(lái)的樹(shù)結(jié)構(gòu)

例如下面這個(gè)組件:

<div id="id">
  A1
  <div id="B1">
    B1
     <div id="C1"></div>
  </div>
  <div id="B2">
      B2
  </div>
</div>

會(huì)使用下面這樣的鏈表表示:

  • 副作用單鏈表
  • 狀態(tài)更新單鏈表
  • ...

鏈表是一種簡(jiǎn)單高效的數(shù)據(jù)結(jié)構(gòu)担败,它在當(dāng)前節(jié)點(diǎn)中保存著指向下一個(gè)節(jié)點(diǎn)的指針,就好像火車(chē)一樣一節(jié)連著一節(jié)

遍歷的時(shí)候镰官,通過(guò)操作指針找到下一個(gè)元素提前。但是操作指針時(shí)(調(diào)整順序和指向)一定要小心。

鏈表相比順序結(jié)構(gòu)數(shù)據(jù)格式的好處就是:

  1. 操作更高效泳唠,比如順序調(diào)整狈网、刪除,只需要改變節(jié)點(diǎn)的指針指向就好了警检。
  2. 不僅可以根據(jù)當(dāng)前節(jié)點(diǎn)找到下一個(gè)節(jié)點(diǎn)孙援,在多向鏈表中,還可以找到他的父節(jié)點(diǎn)或者兄弟節(jié)點(diǎn)扇雕。

但鏈表也不是完美的拓售,缺點(diǎn)就是:

  1. 比順序結(jié)構(gòu)數(shù)據(jù)更占用空間,因?yàn)槊總€(gè)節(jié)點(diǎn)對(duì)象還保存有指向下一個(gè)對(duì)象的指針镶奉。
  2. 不能自由讀取础淤,必須找到他的上一個(gè)節(jié)點(diǎn)崭放。

React 用空間換時(shí)間,更高效的操作可以方便根據(jù)優(yōu)先級(jí)進(jìn)行操作鸽凶。同時(shí)可以根據(jù)當(dāng)前節(jié)點(diǎn)找到其他節(jié)點(diǎn)币砂,在下面提到的掛起和恢復(fù)過(guò)程中起到了關(guān)鍵作用。

React Fiber 是如何實(shí)現(xiàn)更新過(guò)程可控玻侥?

前面講完基本知識(shí)决摧,現(xiàn)在正式開(kāi)始介紹今天的主角 Fiber,看看 React Fiber 是如何實(shí)現(xiàn)對(duì)更新過(guò)程的管控凑兰。

更新過(guò)程的可控主要體現(xiàn)在下面幾個(gè)方面:

  1. 任務(wù)拆分
  2. 任務(wù)掛起掌桩、恢復(fù)、終止
  3. 任務(wù)具備優(yōu)先級(jí)

1. 任務(wù)拆分

前面提到姑食,React Fiber 之前是基于原生執(zhí)行棧波岛,每一次更新操作會(huì)一直占用主線程,直到更新完成音半。這可能會(huì)導(dǎo)致事件響應(yīng)延遲则拷,動(dòng)畫(huà)卡頓等現(xiàn)象。

在 React Fiber 機(jī)制中曹鸠,它采用"化整為零"的戰(zhàn)術(shù)煌茬,將調(diào)和階段(Reconciler)遞歸遍歷 VDOM 這個(gè)大任務(wù)分成若干小任務(wù),每個(gè)任務(wù)只負(fù)責(zé)一個(gè)節(jié)點(diǎn)的處理彻桃。例如:

import React from "react";
import ReactDom from "react-dom"
const jsx = (
    <div id="A1">
    A1
    <div id="B1">
      B1
      <div id="C1">C1</div>
      <div id="C2">C2</div>
    </div>
    <div id="B2">B2</div>
  </div>
)
ReactDom.render(jsx,document.getElementById("root"))

這個(gè)組件在渲染的時(shí)候會(huì)被分成八個(gè)小任務(wù)宣旱,每個(gè)任務(wù)用來(lái)分別處理 A1(div)、A1(text)叛薯、B1(div)、B1(text)笙纤、C1(div)耗溜、C1(text)、C2(div)省容、C2(text)抖拴、B2(div)、B2(text)腥椒。再通過(guò)時(shí)間分片阿宅,在一個(gè)時(shí)間片中執(zhí)行一個(gè)或者多個(gè)任務(wù)。這里提一下笼蛛,所有的小任務(wù)并不是一次性被切分完成洒放,而是處理當(dāng)前任務(wù)的時(shí)候生成下一個(gè)任務(wù),如果沒(méi)有下一個(gè)任務(wù)生成了滨砍,就代表本次渲染的 Diff 操作完成往湿。

2. 掛起妖异、恢復(fù)、終止

再說(shuō)掛起领追、恢復(fù)他膳、終止之前,不得不提兩棵 Fiber 樹(shù)绒窑,workInProgress tree 和 currentFiber tree棕孙。

workInProgress 代表當(dāng)前正在執(zhí)行更新的 Fiber 樹(shù)。在 render 或者 setState 后些膨,會(huì)構(gòu)建一顆 Fiber 樹(shù)蟀俊,也就是 workInProgress tree,這棵樹(shù)在構(gòu)建每一個(gè)節(jié)點(diǎn)的時(shí)候會(huì)收集當(dāng)前節(jié)點(diǎn)的副作用傀蓉,整棵樹(shù)構(gòu)建完成后欧漱,會(huì)形成一條完整的副作用鏈。

currentFiber 表示上次渲染構(gòu)建的 Filber 樹(shù)葬燎。在每一次更新完成后 workInProgress 會(huì)賦值給 currentFiber误甚。在新一輪更新時(shí) workInProgress tree 再重新構(gòu)建,新 workInProgress 的節(jié)點(diǎn)通過(guò) alternate 屬性和 currentFiber 的節(jié)點(diǎn)建立聯(lián)系谱净。

在新 workInProgress tree 的創(chuàng)建過(guò)程中窑邦,會(huì)同 currentFiber 的對(duì)應(yīng)節(jié)點(diǎn)進(jìn)行 Diff 比較,收集副作用壕探。同時(shí)也會(huì)復(fù)用和 currentFiber 對(duì)應(yīng)的節(jié)點(diǎn)對(duì)象冈钦,減少新創(chuàng)建對(duì)象帶來(lái)的開(kāi)銷(xiāo)。也就是說(shuō)無(wú)論是創(chuàng)建還是更新李请,掛起瞧筛、恢復(fù)以及終止操作都是發(fā)生在 workInProgress tree 創(chuàng)建過(guò)程中。workInProgress tree 構(gòu)建過(guò)程其實(shí)就是循環(huán)的執(zhí)行任務(wù)和創(chuàng)建下一個(gè)任務(wù)导盅,大致過(guò)程如下:

image

當(dāng)沒(méi)有下一個(gè)任務(wù)需要執(zhí)行的時(shí)候较幌,workInProgress tree 構(gòu)建完成,開(kāi)始進(jìn)入提交階段白翻,完成真實(shí) DOM 更新乍炉。

在構(gòu)建 workInProgressFiber tree 過(guò)程中可以通過(guò)掛起、恢復(fù)和終止任務(wù)滤馍,實(shí)現(xiàn)對(duì)更新過(guò)程的管控岛琼。下面簡(jiǎn)化了一下源碼,大致實(shí)現(xiàn)如下:

let nextUnitWork = null;//下一個(gè)執(zhí)行單元
//開(kāi)始調(diào)度
function shceduler(task){
     nextUnitWork = task; 
}
//循環(huán)執(zhí)行工作
function workLoop(deadline){
  let shouldYield = false;//是否要讓出時(shí)間片交出控制權(quán)
  while(nextUnitWork && !shouldYield){
    nextUnitWork = performUnitWork(nextUnitWork)
    shouldYield = deadline.timeRemaining()<1 // 沒(méi)有時(shí)間了巢株,檢出控制權(quán)給瀏覽器
  }
  if(!nextUnitWork) {
    conosle.log("所有任務(wù)完成")
    //commitRoot() //提交更新視圖
  }
  // 如果還有任務(wù)槐瑞,但是交出控制權(quán)后,請(qǐng)求下次調(diào)度
  requestIdleCallback(workLoop,{timeout:5000}) 
}
/*
 * 處理一個(gè)小任務(wù),其實(shí)就是一個(gè) Fiber 節(jié)點(diǎn)阁苞,如果還有任務(wù)就返回下一個(gè)需要處理的任務(wù)随珠,沒(méi)有就代表整個(gè)
 */
function performUnitWork(currentFiber){
  ....
  return FiberNode
}

掛起

當(dāng)?shù)谝粋€(gè)小任務(wù)完成后灭袁,先判斷這一幀是否還有空閑時(shí)間,沒(méi)有就掛起下一個(gè)任務(wù)的執(zhí)行窗看,記住當(dāng)前掛起的節(jié)點(diǎn)茸歧,讓出控制權(quán)給瀏覽器執(zhí)行更高優(yōu)先級(jí)的任務(wù)。

恢復(fù)

在瀏覽器渲染完一幀后显沈,判斷當(dāng)前幀是否有剩余時(shí)間软瞎,如果有就恢復(fù)執(zhí)行之前掛起的任務(wù)。如果沒(méi)有任務(wù)需要處理拉讯,代表調(diào)和階段完成涤浇,可以開(kāi)始進(jìn)入渲染階段。這樣完美的解決了調(diào)和過(guò)程一直占用主線程的問(wèn)題魔慷。

那么問(wèn)題來(lái)了他是如何判斷一幀是否有空閑時(shí)間的呢只锭?答案就是我們前面提到的 RIC (RequestIdleCallback) 瀏覽器原生 API,React 源碼中為了兼容低版本的瀏覽器院尔,對(duì)該方法進(jìn)行了 Polyfill蜻展。

當(dāng)恢復(fù)執(zhí)行的時(shí)候又是如何知道下一個(gè)任務(wù)是什么呢?答案在前面提到的鏈表邀摆。在 React Fiber 中每個(gè)任務(wù)其實(shí)就是在處理一個(gè) FiberNode 對(duì)象纵顾,然后又生成下一個(gè)任務(wù)需要處理的 FiberNode。順便提一嘴栋盹,這里提到的FiberNode 是一種數(shù)據(jù)格式施逾,下面是它沒(méi)有開(kāi)美顏的樣子:

class FiberNode {
  constructor(tag, pendingProps, key, mode) {
    // 實(shí)例屬性
    this.tag = tag; // 標(biāo)記不同組件類(lèi)型,如函數(shù)組件例获、類(lèi)組件汉额、文本、原生組件...
    this.key = key; // react 元素上的 key 就是 jsx 上寫(xiě)的那個(gè) key 榨汤,也就是最終 ReactElement 上的
    this.elementType = null; // createElement的第一個(gè)參數(shù)闷愤,ReactElement 上的 type
    this.type = null; // 表示fiber的真實(shí)類(lèi)型 ,elementType 基本一樣件余,在使用了懶加載之類(lèi)的功能時(shí)可能會(huì)不一樣
    this.stateNode = null; // 實(shí)例對(duì)象,比如 class 組件 new 完后就掛載在這個(gè)屬性上面遭居,如果是RootFiber啼器,那么它上面掛的是 FiberRoot,如果是原生節(jié)點(diǎn)就是 dom 對(duì)象
    // fiber
    this.return = null; // 父節(jié)點(diǎn),指向上一個(gè) fiber
    this.child = null; // 子節(jié)點(diǎn)俱萍,指向自身下面的第一個(gè) fiber
    this.sibling = null; // 兄弟組件, 指向一個(gè)兄弟節(jié)點(diǎn)
    this.index = 0; //  一般如果沒(méi)有兄弟節(jié)點(diǎn)的話是0 當(dāng)某個(gè)父節(jié)點(diǎn)下的子節(jié)點(diǎn)是數(shù)組類(lèi)型的時(shí)候會(huì)給每個(gè)子節(jié)點(diǎn)一個(gè) index端壳,index 和 key 要一起做 diff
    this.ref = null; // reactElement 上的 ref 屬性
    this.pendingProps = pendingProps; // 新的 props
    this.memoizedProps = null; // 舊的 props
    this.updateQueue = null; // fiber 上的更新隊(duì)列執(zhí)行一次 setState 就會(huì)往這個(gè)屬性上掛一個(gè)新的更新, 每條更新最終會(huì)形成一個(gè)鏈表結(jié)構(gòu),最后做批量更新
    this.memoizedState = null; // 對(duì)應(yīng)  memoizedProps枪蘑,上次渲染的 state损谦,相當(dāng)于當(dāng)前的 state岖免,理解成 prev 和 next 的關(guān)系
    this.mode = mode; // 表示當(dāng)前組件下的子組件的渲染方式
    // effects
    this.effectTag = NoEffect; // 表示當(dāng)前 fiber 要進(jìn)行何種更新
    this.nextEffect = null; // 指向下個(gè)需要更新的fiber
    this.firstEffect = null; // 指向所有子節(jié)點(diǎn)里,需要更新的 fiber 里的第一個(gè)
    this.lastEffect = null; // 指向所有子節(jié)點(diǎn)中需要更新的 fiber 的最后一個(gè)
    this.expirationTime = NoWork; // 過(guò)期時(shí)間照捡,代表任務(wù)在未來(lái)的哪個(gè)時(shí)間點(diǎn)應(yīng)該被完成
    this.childExpirationTime = NoWork; // child 過(guò)期時(shí)間
    this.alternate = null; // current 樹(shù)和 workInprogress 樹(shù)之間的相互引用
  }
}

額…看著好像有點(diǎn)上頭颅湘,這是開(kāi)了美顏的樣子:

是不是好看多了?在每次循環(huán)的時(shí)候栗精,找到下一個(gè)執(zhí)行需要處理的節(jié)點(diǎn)闯参。

function performUnitWork(currentFiber){
    //beginWork(currentFiber) //找到兒子,并通過(guò)鏈表的方式掛到currentFiber上悲立,每一偶兒子就找后面那個(gè)兄弟
  //有兒子就返回兒子
  if(currentFiber.child){
    return currentFiber.child;
  } 
  //如果沒(méi)有兒子鹿寨,則找弟弟
  while(currentFiber){//一直往上找
    //completeUnitWork(currentFiber);//將自己的副作用掛到父節(jié)點(diǎn)去
    if(currentFiber.sibling){
      return currentFiber.sibling
    }
    currentFiber = currentFiber.return;
  }
}

在一次任務(wù)結(jié)束后返回該處理節(jié)點(diǎn)的子節(jié)點(diǎn)或兄弟節(jié)點(diǎn)或父節(jié)點(diǎn)。只要有節(jié)點(diǎn)返回薪夕,說(shuō)明還有下一個(gè)任務(wù)脚草,下一個(gè)任務(wù)的處理對(duì)象就是返回的節(jié)點(diǎn)。通過(guò)一個(gè)全局變量記住當(dāng)前任務(wù)節(jié)點(diǎn)原献,當(dāng)瀏覽器再次空閑的時(shí)候馏慨,通過(guò)這個(gè)全局變量,找到它的下一個(gè)任務(wù)需要處理的節(jié)點(diǎn)恢復(fù)執(zhí)行嚼贡。就這樣一直循環(huán)下去熏纯,直到?jīng)]有需要處理的節(jié)點(diǎn)返回,代表所有任務(wù)執(zhí)行完成粤策。最后大家手拉手樟澜,就形成了一顆 Fiber 樹(shù)。

終止

其實(shí)并不是每次更新都會(huì)走到提交階段叮盘。當(dāng)在調(diào)和過(guò)程中觸發(fā)了新的更新秩贰,在執(zhí)行下一個(gè)任務(wù)的時(shí)候,判斷是否有優(yōu)先級(jí)更高的執(zhí)行任務(wù)柔吼,如果有就終止原來(lái)將要執(zhí)行的任務(wù)毒费,開(kāi)始新的 workInProgressFiber 樹(shù)構(gòu)建過(guò)程,開(kāi)始新的更新流程愈魏。這樣可以避免重復(fù)更新操作觅玻。這也是在 React 16 以后生命周期函數(shù) componentWillMount 有可能會(huì)執(zhí)行多次的原因。

3. 任務(wù)具備優(yōu)先級(jí)

React Fiber 除了通過(guò)掛起培漏,恢復(fù)和終止來(lái)控制更新外溪厘,還給每個(gè)任務(wù)分配了優(yōu)先級(jí)。具體點(diǎn)就是在創(chuàng)建或者更新 FiberNode 的時(shí)候牌柄,通過(guò)算法給每個(gè)任務(wù)分配一個(gè)到期時(shí)間(expirationTime)畸悬。在每個(gè)任務(wù)執(zhí)行的時(shí)候除了判斷剩余時(shí)間,如果當(dāng)前處理節(jié)點(diǎn)已經(jīng)過(guò)期珊佣,那么無(wú)論現(xiàn)在是否有空閑時(shí)間都必須執(zhí)行改任務(wù)蹋宦。

同時(shí)過(guò)期時(shí)間的大小還代表著任務(wù)的優(yōu)先級(jí)披粟。

任務(wù)在執(zhí)行過(guò)程中順便收集了每個(gè) FiberNode 的副作用,將有副作用的節(jié)點(diǎn)通過(guò) firstEffect冷冗、lastEffect守屉、nextEffect 形成一條副作用單鏈表 AI(TEXT)-B1(TEXT)-C1(TEXT)-C1-C2(TEXT)-C2-B1-B2(TEXT)-B2-A。

其實(shí)最終都是為了收集到這條副作用鏈表贾惦,有了它胸梆,在接下來(lái)的渲染階段就通過(guò)遍歷副作用鏈完成 DOM 更新。這里需要注意须板,更新真實(shí) DOM 的這個(gè)動(dòng)作是一氣呵成的碰镜,不能中斷,不然會(huì)造成視覺(jué)上的不連貫习瑰。

關(guān)于 React Fiber 的思考

1. 能否使用生成器(generater)替代鏈表

在 Fiber 機(jī)制中绪颖,最重要的一點(diǎn)就是需要實(shí)現(xiàn)掛起和恢復(fù),從實(shí)現(xiàn)角度來(lái)說(shuō) generator 也可以實(shí)現(xiàn)甜奄。那么為什么官方?jīng)]有使用 generator 呢柠横?猜測(cè)應(yīng)該是是性能方面的原因。生成器不僅讓您在堆棧的中間讓步课兄,還必須把每個(gè)函數(shù)包裝在一個(gè)生成器中牍氛。一方面增加了許多語(yǔ)法方面的開(kāi)銷(xiāo),另外還增加了任何現(xiàn)有實(shí)現(xiàn)的運(yùn)行時(shí)開(kāi)銷(xiāo)烟阐。性能上遠(yuǎn)沒(méi)有鏈表的方式好搬俊,而且鏈表不需要考慮瀏覽器兼容性。

2. Vue 是否會(huì)采用 Fiber 機(jī)制來(lái)優(yōu)化復(fù)雜頁(yè)面的更新

這個(gè)問(wèn)題其實(shí)有點(diǎn)搞事情蜒茄,如果 Vue 真這么做了是不是就是變相承認(rèn) Vue 是在"集成" Angular 和 React 的優(yōu)點(diǎn)呢唉擂?React 有 Fiber,Vue 就一定要有檀葛?

兩者雖然都依賴(lài) DOM Diff玩祟,但是實(shí)現(xiàn)上且有區(qū)別,DOM Diff 的目的都是收集副作用屿聋。Vue 通過(guò) Watcher 實(shí)現(xiàn)了依賴(lài)收集空扎,本身就是一種很好的優(yōu)化。所以 Vue 沒(méi)有采用 Fiber 機(jī)制润讥,也無(wú)傷大雅转锈。

總結(jié)

React Fiber 的出現(xiàn)相當(dāng)于是在更新過(guò)程中引進(jìn)了一個(gè)中場(chǎng)指揮官,負(fù)責(zé)掌控更新過(guò)程象对,足球世界里管這叫前腰。拋開(kāi)帶來(lái)的性能和效率提升外宴抚,這種“化整為零”和任務(wù)編排的思想勒魔,可以應(yīng)用到我們平時(shí)的架構(gòu)設(shè)計(jì)中甫煞。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市冠绢,隨后出現(xiàn)的幾起案子抚吠,更是在濱河造成了極大的恐慌,老刑警劉巖弟胀,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件楷力,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡孵户,警方通過(guò)查閱死者的電腦和手機(jī)萧朝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)夏哭,“玉大人检柬,你說(shuō)我怎么就攤上這事∈洌” “怎么了何址?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)进胯。 經(jīng)常有香客問(wèn)我用爪,道長(zhǎng),這世上最難降的妖魔是什么胁镐? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任偎血,我火速辦了婚禮,結(jié)果婚禮上希停,老公的妹妹穿的比我還像新娘烁巫。我一直安慰自己,他們只是感情好宠能,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布亚隙。 她就那樣靜靜地躺著,像睡著了一般违崇。 火紅的嫁衣襯著肌膚如雪阿弃。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,708評(píng)論 1 305
  • 那天羞延,我揣著相機(jī)與錄音渣淳,去河邊找鬼。 笑死伴箩,一個(gè)胖子當(dāng)著我的面吹牛入愧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼棺蛛,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼怔蚌!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起旁赊,我...
    開(kāi)封第一講書(shū)人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤桦踊,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后终畅,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體籍胯,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年离福,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了杖狼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡术徊,死狀恐怖本刽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情赠涮,我是刑警寧澤子寓,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站笋除,受9級(jí)特大地震影響斜友,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜垃它,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一鲜屏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧国拇,春花似錦洛史、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至务热,卻和暖如春忆嗜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背崎岂。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工捆毫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人冲甘。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓绩卤,卻偏偏與公主長(zhǎng)得像途样,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子濒憋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容