React Fiber

隨著 React 16 的發(fā)布,Hooks 的正式上線讶凉,很多小伙伴都很興奮辙谜,都想要嘗試這一新的特性,升級(jí) React 的意愿越來越強(qiáng)烈了肝匆。

我們都知道 React 是一個(gè)優(yōu)秀的前端框架粒蜈,很多的大型應(yīng)用都在使用,而作為使用 React 為工具的開發(fā)者也應(yīng)該了解下 React Fiber旗国,F(xiàn)iber 到底是什么枯怖?它能給我們帶來什么?以及 React 團(tuán)隊(duì)為什么要去重寫 Fiber 架構(gòu)能曾?

什么是 React Fiber 度硝?

React Fiber 是 React 16 中新的協(xié)調(diào)引擎,是對(duì)核心算法的一次重新實(shí)現(xiàn)寿冕。

既然是新的蕊程,那 React 團(tuán)隊(duì)為什么要重寫新的 Fiber 架構(gòu)呢?

在 React 16 以前蚂斤,當(dāng)元素較多存捺,需要頻繁刷新的時(shí)候頁面會(huì)出現(xiàn)卡頓,究其原因是因?yàn)楦逻^程是同步的曙蒸,大量的同步計(jì)算任務(wù)阻塞了瀏覽器的渲染。

當(dāng)頁面加載或者更新時(shí)岗钩,React 會(huì)去計(jì)算和比對(duì) Virtual DOM纽窟,最后繪制頁面,整個(gè)過程是同步進(jìn)行的兼吓。當(dāng) JavaScript 在瀏覽器的主線程上長期運(yùn)行臂港,就會(huì)阻塞了樣式計(jì)算、布局和繪制视搏,導(dǎo)致頁面無法得到及時(shí)的更新和響應(yīng)审孽。此時(shí),無論用戶如何點(diǎn)擊鼠標(biāo)或者敲擊鍵盤都不會(huì)得到響應(yīng)浑娜,當(dāng) React 更新完成后剛剛點(diǎn)擊或敲擊的事件才會(huì)得到響應(yīng)佑力。

由于 JavaScript 是單線程的特點(diǎn),所以一個(gè)線程執(zhí)行完成后才會(huì)執(zhí)行下一個(gè)線程筋遭,當(dāng)上一個(gè)線程任務(wù)耗時(shí)太長打颤,程序就會(huì)對(duì)其他輸入不作出響應(yīng)。

React 的更新過程會(huì)先計(jì)算漓滔,一旦任務(wù)開始進(jìn)行编饺,就無法中斷, js 將一直占用主線程响驴, 直到整棵 Virtual DOM 樹計(jì)算完成之后透且,才能把執(zhí)行權(quán)交給渲染引擎,而 React Fiber 就是要改變現(xiàn)狀豁鲤。

React 16 以前的更新過程
它能給我們帶來什么秽誊?
  • 增量渲染

  • 為不同任務(wù)分配優(yōu)先極

  • 更新時(shí)能暫停罕邀、終止、復(fù)用渲染任務(wù)

  • 并發(fā)方面新的能力

接下來养距,讓我們具體來了解下 Fiber诉探。

Fiber

Fiber 把耗時(shí)長的任務(wù)拆分成很多的小片,每個(gè)小片的運(yùn)行時(shí)間很短棍厌,每次只執(zhí)行一個(gè)小片肾胯,執(zhí)行完后看是否還有剩余時(shí)間,如果有就繼續(xù)執(zhí)行下個(gè)小片耘纱,如果沒有就掛起當(dāng)前任務(wù)敬肚,將控制權(quán)交給 React 負(fù)責(zé)任務(wù)協(xié)調(diào)的模塊,看有沒有其他緊急任務(wù)要做束析,如果沒有就繼續(xù)更新當(dāng)前任務(wù)艳馒,如果有緊急任務(wù)就去做緊急任務(wù),等主線程不忙的時(shí)候在繼續(xù)執(zhí)行當(dāng)前任務(wù)员寇。

分片之后弄慰,每執(zhí)行一段時(shí)間,都會(huì)將控制權(quán)交給主線程蝶锋。

這樣唯一的線程不會(huì)被獨(dú)占陆爽,其他任務(wù)依然有運(yùn)行的機(jī)會(huì)。

這種策略叫做 Cooperative Scheduling(合作式調(diào)度)扳缕,操作系統(tǒng)常用任務(wù)調(diào)度策略之一慌闭。

總而言之,我們了解到躯舔,F(xiàn)iber 是一個(gè)最小工作單元驴剔,也是堆棧的重新實(shí)現(xiàn),可以理解為是一個(gè)虛擬的堆棧幀粥庄。它將可中斷的任務(wù)拆分成多個(gè)任務(wù)丧失,通過優(yōu)先級(jí)來自由調(diào)度子任務(wù),分段更新飒赃,從而將之前的同步渲染改為異步渲染利花。

Fiber 結(jié)構(gòu)

維護(hù)每一個(gè)分片的數(shù)據(jù)結(jié)構(gòu),就是 Fiber载佳,它可以用 JS 對(duì)象來表示炒事,其中包含有關(guān)組件,以及輸入和輸出的信息:

const fiber = {
  // 跟當(dāng)前 Fiber 相關(guān)本地狀態(tài)(比如瀏覽器環(huán)境就是 DOM 節(jié)點(diǎn))
  stateNode: any, // 節(jié)點(diǎn)實(shí)例
  // 指向他在 Fiber 節(jié)點(diǎn)樹中的 `parent`蔫慧,用來在處理完這個(gè)節(jié)點(diǎn)之后向上返回
  return: Fiber | null, // 父節(jié)點(diǎn)
  // 單鏈表樹結(jié)構(gòu)
  child: Fiber | null, // 指向自己的第一個(gè)子節(jié)點(diǎn)
  sibling: Fiber | null, // 指向自己的兄弟結(jié)構(gòu)挠乳,兄弟節(jié)點(diǎn)的return指向同一個(gè)父節(jié)點(diǎn)
  index: number,

  // 組件相關(guān)
  tag: WorkTag, // 標(biāo)記不同的組件類型
  key: null | string, // ReactElement 里面的 key,與 type 一起,主要用來在reconciliation 期間確定 Fiber 是否可重用睡扬。
  elementType: any, // ReactElement.type盟蚣,也就是我們調(diào)用 `createElement` 的第一個(gè)參數(shù)
  type: any,  // 對(duì)于復(fù)合組件,type 是函數(shù)或者是類組件(`function`或者`class`)卖怜,對(duì)于標(biāo)準(zhǔn)組件(div或者span)屎开,type 是 string
    
  // ref屬性
  ref: null | (((handle: mixed) => void) & {_stringRef: ?string}) | RefObject,

  // 更新相關(guān)
  // 當(dāng)傳入的 pendingProps 和 memoizedProps 相同的時(shí)候,表示 fiber 可以重新使用之前的 fiber马靠,以避免重復(fù)的工作奄抽。
  pendingProps: any,  // 新的變動(dòng)帶來的新的props
  memoizedProps: any,  // 上一次渲染完成之后的props
  updateQueue: UpdateQueue<any> | null,  // 該Fiber對(duì)應(yīng)的組件產(chǎn)生的Update會(huì)存放在這個(gè)隊(duì)列里面
  memoizedState: any, // 上一次渲染的時(shí)候的state
  firstContextDependency: ContextDependency<mixed> | null, // 一個(gè)列表,存放這個(gè)Fiber依賴的context
  pendingWorkPriority: number, // 待處理的工作優(yōu)先級(jí)甩鳄,除 NoWork 為0外逞度,數(shù)字越大表示優(yōu)先級(jí)越低。
    
  // Scheduler 相關(guān)
  expirationTime: ExpirationTime,  // 代表任務(wù)在未來的哪個(gè)時(shí)間點(diǎn)應(yīng)該被完成妙啃,不包括他的子樹產(chǎn)生的任務(wù)
  // 快速確定子樹中是否有不在等待的變化
  childExpirationTime: ExpirationTime,

  // 用來描述當(dāng)前Fiber和他子樹的`Bitfield`
  // 共存的模式表示這個(gè)子樹是否默認(rèn)是異步渲染的
  // Fiber被創(chuàng)建的時(shí)候他會(huì)繼承父Fiber
  // 其他的標(biāo)識(shí)也可以在創(chuàng)建的時(shí)候被設(shè)置
  // 但是在創(chuàng)建之后不應(yīng)該再被修改档泽,特別是他的子Fiber創(chuàng)建之前
  mode: TypeOfMode,
    
  // 在Fiber樹更新的過程中,每個(gè)Fiber都會(huì)有一個(gè)跟其對(duì)應(yīng)的Fiber
  // 我們稱他為`current <==> workInProgress`
  // 在渲染完成之后他們會(huì)交換位置
  alternate: Fiber | null,

  // Effect 相關(guān)的
  effectTag: SideEffectTag, // 用來記錄Side Effect
  nextEffect: Fiber | null, // 單鏈表用來快速查找下一個(gè)side effect
  firstEffect: Fiber | null,  // 子樹中第一個(gè)side effect
  lastEffect: Fiber | null, // 子樹中最后一個(gè)side effect
}
任務(wù)的優(yōu)先級(jí)

上面講到了任務(wù)的執(zhí)行是根據(jù)優(yōu)先級(jí)來調(diào)度的揖赴,那我們現(xiàn)在具體了解一下優(yōu)先級(jí)馆匿。

  • synchronous 同步執(zhí)行,首屏使用

  • task 在 next tick 之前執(zhí)行

  • animation 下一幀之前執(zhí)行储笑,通過requestAnimationFrame來調(diào)度甜熔,這樣在下一幀就能立即開始動(dòng)畫過程

  • high 在不久的將來立即執(zhí)行

  • low 稍微延遲(100-200ms)執(zhí)行也沒關(guān)系

  • offscreen 當(dāng)前隱藏的、屏幕外的(看不見的)元素突倍,在下一次 render 時(shí)或 scroll 時(shí)才執(zhí)行

Fiber reconciler

Fiber Reconciler 決定了當(dāng)任務(wù)調(diào)度完成之后,如何去執(zhí)行每個(gè)任務(wù)盆昙,如何去更新每一個(gè)節(jié)點(diǎn)的過程羽历。

React Fiber 更新過程分為兩個(gè)階段

  • 第一階段 Reconciliation Phase,生成 Fiber 樹淡喜,得出需要更新的節(jié)點(diǎn)信息秕磷。是一個(gè)漸進(jìn)的過程,可以被打斷炼团。可能會(huì)調(diào)用 componentWillMount瘟芝,componentWillReceiveProps易桃,shouldComponentUpdate,componentWillUpdate 生命周期函數(shù)锌俱。

  • 第二階段 Commit Phase晤郑,將需要更新的節(jié)點(diǎn)批量更新,這個(gè)過程不能被打斷≡烨蓿可能會(huì)調(diào)用 componentDidMount磕洪,componentDidUpdate,componentWillUnmount 生命周期函數(shù)诫龙。

Fiber 更新過程
Fiber tree 與 WorkInProgress Tree
// 單鏈表樹結(jié)構(gòu)
{
   return: Fiber | null, // 指向父節(jié)點(diǎn)
   child: Fiber | null, // 指向自己的第一個(gè)子節(jié)點(diǎn)
   sibling: Fiber | null, // 指向自己的兄弟結(jié)構(gòu)析显,兄弟節(jié)點(diǎn)的 return 指向同一個(gè)父節(jié)點(diǎn)
}

首次渲染之后 React 會(huì)得到一個(gè) Fiber 樹,也就是 Current tree(當(dāng)前樹)签赃。當(dāng)處理更新的時(shí)候谷异,React 會(huì)構(gòu)建 WorkInProgress Tree(工作過程樹),當(dāng)構(gòu)造完成后會(huì)將 current 指針指向 WorkInProgress Tree姊舵,WorkInProgress Tree 成了新的 Fiber tree晰绎。

這被稱做雙緩沖。以 Fiber tree 為主括丁,WorkInProgress Tree 為輔荞下。

雙緩沖技術(shù)可以復(fù)用內(nèi)部對(duì)象(fiber),節(jié)省內(nèi)存分配史飞、GC的時(shí)間開銷尖昏。

總結(jié)

本篇文章篇幅有限,大致講了 Fiber 的相關(guān)知識(shí)和 React 的工作流程构资,對(duì)于細(xì)節(jié)抽诉,比如:如何調(diào)度任務(wù),如何 diff 等吐绵,感興趣的同學(xué)可以自行結(jié)合源碼研究分析迹淌。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市己单,隨后出現(xiàn)的幾起案子唉窃,更是在濱河造成了極大的恐慌,老刑警劉巖纹笼,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纹份,死亡現(xiàn)場離奇詭異,居然都是意外死亡廷痘,警方通過查閱死者的電腦和手機(jī)蔓涧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來笋额,“玉大人元暴,你說我怎么就攤上這事×墼桑” “怎么了昨寞?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵瞻惋,是天一觀的道長。 經(jīng)常有香客問我援岩,道長歼狼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任享怀,我火速辦了婚禮羽峰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘添瓷。我一直安慰自己梅屉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布鳞贷。 她就那樣靜靜地躺著坯汤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪搀愧。 梳的紋絲不亂的頭發(fā)上惰聂,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音咱筛,去河邊找鬼搓幌。 笑死,一個(gè)胖子當(dāng)著我的面吹牛迅箩,可吹牛的內(nèi)容都是我干的溉愁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼饲趋,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼拐揭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起奕塑,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤投队,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后爵川,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡息楔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年寝贡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片值依。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡圃泡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出愿险,到底是詐尸還是另有隱情颇蜡,我是刑警寧澤价说,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站风秤,受9級(jí)特大地震影響鳖目,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缤弦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一领迈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧碍沐,春花似錦狸捅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至斋陪,卻和暖如春朽褪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鳍贾。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來泰國打工鞍匾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人骑科。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓橡淑,卻偏偏與公主長得像,于是被迫代替她去往敵國和親咆爽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子梁棠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

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

  • 原文鏈接https://blog.csdn.net/weixin_34253126/article/details...
    雷霆克呂齊閱讀 216評(píng)論 0 1
  • 前言 React Fiber 不是一個(gè)新的東西,但在前端領(lǐng)域是第一次廣為認(rèn)知的應(yīng)用斗埂。幾年前全新的Fiber架構(gòu)讓剛...
    這個(gè)前端不太冷閱讀 5,520評(píng)論 2 8
  • > 本文重點(diǎn):介紹React重構(gòu)的起因和目的符糊,理解Fiber tree單向鏈表結(jié)構(gòu)中各屬性含義,梳理調(diào)度過程和核心...
    intopiece_檳閱讀 1,213評(píng)論 0 0
  • 前言 隨著mvvm模式的流行呛凶,現(xiàn)在大多數(shù)的前端框架基本都是在react和vue中選擇其一男娄,react的核心就是盡量...
    鄒小鄒大廚閱讀 485評(píng)論 0 1
  • 上次寫了react整體框架的理解,這次想寫寫看對(duì)于新版React的新的React Fiber的實(shí)現(xiàn)漾稀。 在React...
    離開North閱讀 1,597評(píng)論 1 2