前天在油管上看了大神的一個關於js優化的分享,佩服的五體投地。可是視頻又過短了,就20來分鐘,有點意猶未盡的意思,還好視頻結尾放了幾篇大神寫的關於react fiber的文章,因此趕忙找來學習了一下。html
fiber node是react內部用來表明一些須要完成的工做的一種數據結構,每個react element都會對應一個fiber node。以前react15裏的vdom tree如今就變成了一個fiber tree,每個fiber node都保存着指向其sibling, child 和 parent的指針。因此fiber tree大概就是一個雙向的linked list吧。node
既然已經有了vdom tree,爲啥又搞了這麼個fiber tree?react
我沒細讀過源碼,不過從官方文檔,大概由於vdom tree的遍歷採用了遞歸的寫法。 遞歸一旦開始,在結束以前,都不受控了。也就是說,若是react觸發一次update,那麼在update完成以前,瀏覽器主線程啥都幹不了。。 若是這個update耗時比較久,那就會形成卡頓。git
因此最大的問題就是遍歷起來不受控了。。想要控制咋辦?拿while來改寫遞歸唄。因此react組裏的大佬們就搞出了這麼個"fiber linked list"。github
fiber的工做大概能夠分爲兩個階段: 1. render 2. commit瀏覽器
在第一次mount以後,react會爲每個渲染的react element都創建一個fiber node,並組成一顆樹(current)。當更新觸發的時候,react會創建另一顆樹(workInProgress),全部的updates都會反映在這顆樹上。而且兩顆樹之間的節點都有一個alternate的屬性,指向本身在另外一顆樹上的映射。在這個階段,react會在workInProgress的節點上更新state和props,但並不會作任何帶有side effect的操做(留給commit階段),這裏react只會給每一個節點記錄在commit階段到底應該作些啥操做,這個信息保存在effectTag字段上。數據結構
除了構建workInProgress樹以外,爲了提升性能,react 還會維護一個effect list。就是用全部有sideeffect操做的節點構建一個linkedlist。 等真的到了要執行這些操做的時候,直接掃這個list就行了,而不用去遍歷整個樹。dom
除了上面的工做,render phase還會調用如下的函數:異步
render階段生成了current樹,workInProgress樹和effect list,接下來就進入到commit 階段。async
在commit階段,react會直接遍歷effect list,完成每一個節點的操做。在全部節點完成以後,用workInProgress樹來替換current樹,舊的current樹就能夠被垃圾回收了。
在commit階段,除了完成每一個節點的工做外,還會跑這幾個函數:
那麼問題來了, 爲啥react要大費周章的搞這麼兩個階段呢?
緣由是在render phase階段,全部操做能夠是異步的。 react會把遍歷整顆樹的任務切成小任務,放在requestIdleCallback裏面來執行。 可是每次在暫停以後,下一次再繼續的時候,有可能會放棄掉以前作的全部任務重頭來過(這個的緣由不是特別清楚,多是在兩個任務以前,某個狀態改變了,以前的任務可能已經失效了?),因此全部會產生反作用的操做都不能放在這個階段執行。 react 16.3把不少原來經常使用的生命週期標記爲unsafe也是這個道理。就是由於react的大佬們發現使用者老在這些生命週期函數寫一些帶反作用的邏輯。這種寫法在fiber的體系下問題就很大(可能會被執行屢次), 官方的這篇文章把這個事情也說的特別清楚了。
而commit phase則相反,全部的操做都會一口氣作完(同步)。因此啊,在commit phase調用的生命週期裏面,最好仍是不要作過重的活爲好。。
React Fiber Architecture The how and why on React’s usage of linked list in Fiber to walk the component’s tree
Inside Fiber: in-depth overview of the new reconciliation algorithm in React