媒介
Facebook 的研發才能真是驚人, Fiber
架構給 React 帶來了新視野的同時,將調理一詞引見給了前端,然則這個架構着實不好懂,比起之前的 Vdom
樹,新的 Fiber
樹就貧苦太多。
可以說,React 16 和 React 15 已是技能上的分水嶺,然則得益於 React 16 的 Fiber
架構,使得 React 縱然在沒有開啟異步的狀況下,機能照舊是得到了進步。
經由兩個禮拜的痛楚研討,終究將 React 16 的襯着頭緒摸得比較清楚,可以寫文章來紀錄、回憶一下。
假如你已輕微明白了 Fiber
架構,可以直接看代碼:堆棧地點
什麼是 React Fiber ?
React Fiber
並非所謂的纖程(微線程、協程),而是一種基於瀏覽器的單線程調理算法,背地的支撐 API 是赫赫有名的: requestIdleCallback
,得到了這個 API 的支撐,我們便可以將 React 中最耗時的部份放入个中。
回憶 React 歷年來的算法都曉得,reconcilation
算法實際上是一個大遞歸,大遞歸一旦舉行,想要中綴照樣比較不好操縱的,加上頭大尾大的 React 15 代碼已膨脹到了難以想象的田地,在重重壓力之下,React 運用了大輪迴來替代之前的大遞歸,雖然代碼變得比遞歸難懂了幾個梯度,然則實際上,代碼量比本來少了異常多(開闢版本 3W 行緊縮到了 1.3W 行)
那題目就來了,什麼是 Fiber
:一種將 recocilation
(遞歸 diff
),拆分紅無數個小使命的算法;它隨時可以住手,恢復。住手恢復的機遇取決於當前的一幀( 16ms
)內,另有無充足的時刻許可盤算。
React 異步襯着流程圖
- 用戶挪用
ReactDOM.render
要領,傳入比方<App />
組件,React 最先運作<App />
-
<App />
在內部會被轉換成RootFiber
節點,一個特別的節點,並紀錄在一個全局變量中,TopTree
- 拿到
<App />
的RootFiber
,起首建立一個<App />
對應的 Fiber ,然後加上 Fiber 信息,以便今後回溯。隨後,賦值給之前的全局變量 TopTree - 運用
requestIdleCallback
重複第三個步驟,直到輪迴到樹的一切節點 - 末了完成了
diff
階段,一次性將變化更新到實在DOM
中,以防備 UI 展現的不一連性
个中,重點就是 3
和 4
階段,這兩個階段將建立實在 DOM 和組件襯着 ( render
)拆分為無數的小碎塊,運用 requestIdleCallback
一連舉行。在 React 15 的時刻,襯着、建立、插進去、刪除等操縱是最費時的,在 React 16 中將襯着、建立抽離出來分片,如許機能就得到了極大的提拔。
那為何更新到實在 DOM 中不能拆分呢?理論上來講,是可以拆分的,然則這會形成 UI 的不一連性,極大的影響體驗。
遞歸變成了輪迴
以簡樸的組件為例子:
- 從頂端的
div#root
向下走,先走左子樹 -
div
有兩個孩子span
,繼承走左側的 - 來到
span
,之下只要一個hello
,到此,不再繼承往下,而是往上回到span
- 由於
span
有一個兄弟,因而往兄弟span
走去 - 兄弟
span
有孩子luy
,到此,不繼承往下,而是回到luy
的老爹span
-
luy
的老爹span
右側沒有兄弟了,因而回到其老爹div
-
div
沒有任何的兄弟,因而回到頂端的div#root
每經由一個 Fiber
節點,實行 render
或許 document.createElement
(或許更新 DOM
)的操縱
Fiber 數據結構
一個 Fiber
數據結構比較龐雜
const Fiber = {
tag: HOST_COMPONENT,
type: 'div',
return: parentFiber,
child: childFiber,
sibling: null,
alternate: currentFiber,
stateNode: document.createElement('div') | instance,
props: { children: [], className: 'foo' },
partialState: null,
effectTag: PLACEMENT,
effects: []
}
這是一個比較完整的 Fiber object
,他龐雜的緣由是由於一個 Fiber
就代表了一個「正在實行或許實行終了」的操縱單位。這個觀點不是那末好明白,假如要說得簡樸一點就是:之前的 VDOM
樹節點的升級版。讓我們引見幾個癥結屬性:
- 由「 遞歸改輪迴 」我們可以得知,當我們輪迴的遍歷樹抵達底部時,需要回到其父節點,那末對應的就是
Fiber
中的return
屬性(之前叫parent
)。child
和sibling
相似,代表這個Fiber
的子Fiber
和兄弟Fiber
-
stateNode
這個屬性比較特別,用於紀錄當前Fiber
所對應的實在DOM
節點 或許 當前假造組件的實例,這麼做的緣由第一是為了完成Ref
,第二是為了完成DOM
的跟蹤 -
tag
屬性在新版的React
中一共有 14 種值,離別代表了差別的JSX
範例。 -
effectTag
和effects
這兩個屬性為的是紀錄每一個節點Diff
后需要變動的狀況,比方刪除,挪動,插進去,替代,更新等…
alternate
屬性我想拿出來零丁說一下,這個屬性是 Fiber
架構新到場的屬性。我們都曉得,VDOM
算法是在更新的時刻天生一顆新的 VDOM
樹,去和舊的舉行對照。在 Fiber
架構中,當我們挪用 ReactDOM.render
或許 setState
今後,會天生一顆樹叫做:work-in-progress tree
,這一顆樹就是我們所謂的新樹用來與我們的舊樹舉行對照,新的樹和舊的樹的 Fiber
是完整不一樣的,此時,我們就需要 alternate
屬性去鏈接新樹和舊樹。
司徒正美的研討中,一個 Fiber
和它的 alternate
屬性構成了一個聯嬰體,他們有配合的 tag
,type
,stateNode
屬性,這些屬性在毛病邊境自爆時,用於恢復當前節點。
最先寫代碼:Component 組織函數
講了那末多的理論,人人肯定是暈了,然則沒辦法,Fiber
架構已比之前的簡樸 React 要龐雜太多了,因而不可能希望一次性把 Fiber
的內容悉數明白,需要重複多看。
固然,連繫代碼來梳理,思緒舊越發清楚了。我們在構建新的架構時,老的 Luy 代碼大部份都要舉行重構了,先來看看幾個主要重構的處所:
export class Component {
constructor(props, context) {
this.props = props
this.context = context
this.state = this.state || {}
this.refs = {}
this.updater = {}
}
setState(updater) {
scheduleWork(this, updater)
}
render() {
throw 'should implement `render()` function'
}
}
Component.prototype.isReactComponent = true
- 這就是
React.Component
的代碼 - 組織函數中,我們都進兩個參數,一個是外部的
props
,一個是context
- 內部有
state
,refs
,updater
,updater
用於網絡setState
的信息,便於今后更新用。固然,在這個版本當中,我並沒有運用。 -
setState
函數也並沒有做行列處置懲罰,只是挪用了scheduleWork
這個函數 -
Component.prototype.isReactComponent = true
,這段代碼錶飾着,假如一個組件的範例為function
且具有isReactComponent
,那末他就是一個有狀況組件,在建立實例時需要用new
,而無狀況組件只需要fn(props,context)
挪用
const tag = {
HostComponent: 'host',
ClassComponent: 'class',
HostRoot: 'root',
HostText: 6,
FunctionalComponent: 1
}
const updateQueue = []
export function render(Vnode, Container, callback) {
updateQueue.push({
fromTag: tag.HostRoot,
stateNode: Container,
props: { children: Vnode }
})
requestIdleCallback(performWork) //最先幹活
}
export function scheduleWork(instance, partialState) {
updateQueue.push({
fromTag: tag.ClassComponent,
stateNode: instance,
partialState: partialState
})
requestIdleCallback(performWork) //最先幹活
}
我們定義了一個全局變量 updateQueue
來紀錄我們一切的更新操縱,每當 render
和 scheduleWork (setState)
觸發時,我們都邑往 updateQueue
中 push
一個狀況,然後,進而挪用赫赫有名的 requestIdleCallback
舉行更新。在這裏與之前的 react 15 最大差別是,更新階段和初次襯着階段得到了一致,都是運用了 updateQueue
舉行更新。
實際上這裏另有優化的空間,就是屢次 setState
的時刻,應當合併成一次再舉行 requestIdleCallback
的挪用,不過這並非我們的目的,我們的目的是搞懂 Fiber
架構。requestIdleCallback
挪用的是 performWork
函數,我們接下來看看
performWork 函數
const EXPIRATION_TIME = 1 // ms async 過期時刻
let nextUnitOfWork = null
let pendingCommit = null
function performWork(deadline) {
workLoop(deadline)
if (nextUnitOfWork || updateQueue.length > 0) {
requestIdleCallback(performWork) //繼承干
}
}
function workLoop(deadline) {
if (!nextUnitOfWork) {
//一個周期內只建立一次
nextUnitOfWork = createWorkInProgress(updateQueue)
}
while (nextUnitOfWork && deadline.timeRemaining() > EXPIRATION_TIME) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork)
}
if (pendingCommit) {
//當全局 pendingCommit 變量被負值
commitAllwork(pendingCommit)
}
}
熟習 requestIdleCallback
的同硯肯定對這兩個函數並不生疏,這兩個函數實在做的就是所謂的異步調理。
performWork
函數主要做了兩件事,第一件事就是拿到 deadline
進入我們之前所謂的大輪迴,也就是正式進入處置懲罰新舊 Fiber
的 Diff
階段,這個階段比較的巧妙,我們叫他 workLoop
階段。workLoop
會一次處置懲罰 1 個或許多個 Fiber
,詳細處置懲罰若干個,要看每一幀詳細還剩下若干時刻,假如一個 Fiber
斲喪太多時刻,那末就會比及下一幀再處置懲罰下一個 Fiber
,云云輪迴,遍歷全部 VDOM
樹。
在這裏我們注重到,假如一個
Fiber
斲喪太多時刻,可能會致使一幀時刻的過期,不過實在沒什麼題目啦,也僅僅是一幀過期罷了,關於我們視覺上並沒有多大的影響。
workLoop
函數重如果三部曲:
-
createWorkInProgress
這個函數會構建一顆樹的頂端,賦值給全局變量nextUnitOfWork
,經由歷程迭代的體式格局,不停更新nextUnitOfWork
直到遍歷完一切樹的節點。 -
performUnitOfWork
函數是第二步,不停的檢測當前幀是不是還剩餘時刻,舉行WorkInProgress
tree 的迭代 - 當
WorkInProgress
tree 迭代終了今後,挪用commitAllWork
,將一切的變動悉數一次性的更新到DOM
中,以保證 UI 的一連性
一切的 Diff
和建立實在 DOM
的操縱,都在 performUnitOfWork
當中,然則插進去和刪除是在 commitAllWork
當中。接下來,我們一一剖析三部曲的內部操縱。
第一步:createWorkInProgress
export function createWorkInProgress(updateQueue) {
const updateTask = updateQueue.shift()
if (!updateTask) return
if (updateTask.partialState) {
// 證實這是一個setState操縱
updateTask.stateNode._internalfiber.partialState = updateTask.partialState
}
const rootFiber =
updateTask.fromTag === tag.HostRoot
? updateTask.stateNode._rootContainerFiber
: getRoot(updateTask.stateNode._internalfiber)
return {
tag: tag.HostRoot,
stateNode: updateTask.stateNode,
props: updateTask.props || rootFiber.props,
alternate: rootFiber // 用於鏈接新舊的 VDOM
}
}
function getRoot(fiber) {
let _fiber = fiber
while (_fiber.return) {
_fiber = _fiber.return
}
return _fiber
這個函數的主要作用就是構建 workInProgress
樹的頂端並賦值給全局變量 nextUnitOfWork。
起首,我們先從 updateQueue
中獵取一個使命對象 updateTask
。隨後,舉行推斷是不是是更新階段。然後獵取 workInProgress
樹的頂端。假如是第一次襯着, RootFiber
的值是空的,由於我們並沒有構建任何的樹。
末了,我們將返回一個 Fiber
對象,這個 Fiber
對象的標識符( tag
)是 HostRoot
。
第二步:performUnitOfWork
// 最先遍歷
function performUnitOfWork(workInProgress) {
const nextChild = beginWork(workInProgress)
if (nextChild) return nextChild
// 沒有 nextChild, 我們看看這個節點有無 sibling
let current = workInProgress
while (current) {
//網絡當前節點的effect,然後向上通報
completeWork(current)
if (current.sibling) return current.sibling
//沒有 sibling,回到這個節點的父親,看看有無sibling
current = current.return
}
}
我們挪用 performUnitOfWork
處置懲罰我們的 workInProgress
。
全部函數做的事變實在就是一個左遍歷樹的歷程。起首,我們挪用 beginWork
,取得一個當前 Fiber
下的第一個孩子,假如有直接返回出去給 nextUnitOfWork
,看成下一個處置懲罰的節點;假如沒有找到任何孩子,證實我們已抵達了樹的底部,經由歷程下面的 while
輪迴,回到當前節點的父節點,將當前 Fiber
下具有 Effect
的孩子悉數紀錄下來,以便於今后更新 DOM
。
然後查找當前節點的父親節點,是不是有兄弟,有就返回,當做下一個處置懲罰的節點,假如沒有,就繼承回溯。
全部歷程用圖來示意,就是:
在議論第三部之前,我們依然有兩個疑惑的處所:
-
beginWork
是怎樣建立孩子的 -
completeWork
是怎樣網絡effect
的接下來,我們就來一同看看
beginWork
function beginWork(currentFiber) {
switch (currentFiber.tag) {
case tag.ClassComponent: {
return updateClassComponent(currentFiber)
}
case tag.FunctionalComponent: {
return updateFunctionalComponent(currentFiber)
}
default: {
return updateHostComponent(currentFiber)
}
}
}
function updateHostComponent(currentFiber) {
// 當一個 fiber 對應的 stateNode 是原生節點,那末他的 children 就放在 props 里
if (!currentFiber.stateNode) {
if (currentFiber.type === null) {
//代表這是筆墨節點
currentFiber.stateNode = document.createTextNode(currentFiber.props)
} else {
//代表這是實在原生 DOM 節點
currentFiber.stateNode = document.createElement(currentFiber.type)
}
}
const newChildren = currentFiber.props.children
return reconcileChildrenArray(currentFiber, newChildren)
}
function updateFunctionalComponent(currentFiber) {
let type = currentFiber.type
let props = currentFiber.props
const newChildren = currentFiber.type(props)
return reconcileChildrenArray(currentFiber, newChildren)
}
function updateClassComponent(currentFiber) {
let instance = currentFiber.stateNode
if (!instance) {
// 假如是 mount 階段,構建一個 instance
instance = currentFiber.stateNode = createInstance(currentFiber)
}
// 將新的state,props刷給當前的instance
instance.props = currentFiber.props
instance.state = { ...instance.state, ...currentFiber.partialState }
// 清空 partialState
currentFiber.partialState = null
const newChildren = currentFiber.stateNode.render()
// currentFiber 代表老的,newChildren代表新的
// 這個函數會返回孩子行列的第一個
return reconcileChildrenArray(currentFiber, newChildren)
}
beginWork
實際上是一個推斷分支的函數,全部函數的意義是:
- 推斷當前的
Fiber
是什麼範例,是class
的走class
分支,是stateless
的走stateless,是原生節點的走原生分支
- 假如沒有
stateNode
,則建立一個stateNode
- 假如是
class
,則建立實例,挪用render
函數,襯着其兒子;假如是原生節點,挪用DOM API
建立原生節點;假如是stateless
,就實行它,襯着出VDOM
節點 - 末了,走到最主要的函數,
recocileChildrenArray
函數,將其每一個孩子舉行鏈表的鏈接,舉行diff
,然後返回當前Fiber
之下的第一個孩子
我們來看看比較主要的 classComponent
的構建流程
function updateClassComponent(currentFiber) {
let instance = currentFiber.stateNode
if (!instance) {
// 假如是 mount 階段,構建一個 instance
instance = currentFiber.stateNode = createInstance(currentFiber)
}
// 將新的state,props刷給當前的instance
instance.props = currentFiber.props
instance.state = { ...instance.state, ...currentFiber.partialState }
// 清空 partialState
currentFiber.partialState = null
const newChildren = currentFiber.stateNode.render()
// currentFiber 代表老的,newChildren代表新的
// 這個函數會返回孩子行列的第一個
return reconcileChildrenArray(currentFiber, newChildren)
}
function createInstance(fiber) {
const instance = new fiber.type(fiber.props)
instance._internalfiber = fiber
return instance
}
假如是初次襯着,那末組件並沒有被實例話,此時我們挪用 createInstance
實例化組件,然後將當前的 props
和 state
賦值給 props
、state
,隨後我們挪用 render
函數,取得了新兒子 newChildren
。
襯着出新兒子今後,來到了新架構下最主要的中心函數 reconcileChildrenArray
.
reconcileChildrenArray
const PLACEMENT = 1
const DELETION = 2
const UPDATE = 3
function placeChild(currentFiber, newChild) {
const type = newChild.type
if (typeof newChild === 'string' || typeof newChild === 'number') {
// 假如這個節點沒有 type ,這個節點就多是 number 或許 string
return createFiber(tag.HostText, null, newChild, currentFiber, PLACEMENT)
}
if (typeof type === 'string') {
// 原生節點
return createFiber(tag.HOST_COMPONENT, newChild.type, newChild.props, currentFiber, PLACEMENT)
}
if (typeof type === 'function') {
const _tag = type.prototype.isReactComponent ? tag.CLASS_COMPONENT : tag.FunctionalComponent
return {
type: newChild.type,
tag: _tag,
props: newChild.props,
return: currentFiber,
effectTag: PLACEMENT
}
}
}
function reconcileChildrenArray(currentFiber, newChildren) {
// 對照節點,雷同的標記更新
// 差別的標記 替代
// 過剩的標記刪除,而且紀錄下來
const arrayfiyChildren = arrayfiy(newChildren)
let index = 0
let oldFiber = currentFiber.alternate ? currentFiber.alternate.child : null
let newFiber = null
while (index < arrayfiyChildren.length || oldFiber !== null) {
const prevFiber = newFiber
const newChild = arrayfiyChildren[index]
const isSameFiber = oldFiber && newChild && newChild.type === oldFiber.type
if (isSameFiber) {
newFiber = {
type: oldFiber.type,
tag: oldFiber.tag,
stateNode: oldFiber.stateNode,
props: newChild.props,
return: currentFiber,
alternate: oldFiber,
partialState: oldFiber.partialState,
effectTag: UPDATE
}
}
if (!isSameFiber && newChild) {
newFiber = placeChild(currentFiber, newChild)
}
if (!isSameFiber && oldFiber) {
// 這個狀況的意義是新的節點比舊的節點少
// 這時刻,我們要將變動的 effect 放在本節點的 list 里
oldFiber.effectTag = DELETION
currentFiber.effects = currentFiber.effects || []
currentFiber.effects.push(oldFiber)
}
if (oldFiber) {
oldFiber = oldFiber.sibling || null
}
if (index === 0) {
currentFiber.child = newFiber
} else if (prevFiber && newChild) {
// 這裏不懂是幹嗎的
prevFiber.sibling = newFiber
}
index++
}
return currentFiber.child
}
這個函數做了幾件事
- 將孩子
array
化,這麼做可以使得react
的render
函數返回數組 -
currentFiber
是新的workInProgress
上的一個節點,是屬於新的VDOM
樹 ,而此時,我們必需要找到舊的VDOM
樹來舉行比對。那末在這裏,Alternate
屬性就起到了癥結性作用,這個屬性鏈接了舊的VDOM
,使得我們可以獵取本來的VDOM
- 接下來我們舉行對照,假如新的節點的
type
與本來的雷同,那末我們將新建一個Fiber
,標記這個Fiber
為UPDATE
- 假如新的節點的
type
與本來的不雷同,那我們運用PALCEMENT
來標記他 - 假如舊的節點數目比新的節點少,那就證實,我們要刪除舊的節點,我們把舊節點標記為
DELETION
,並構建一個effect list
紀錄下來 - 當前遍歷的是組件的第一個孩子,那末我們將他紀錄在
currentFiber
的child
字段中 - 當遍歷的不是第一個孩子,我們將 新建的
newFiber
用鏈表的情勢將他們一同推入到currentFiber
中 - 返回當前
currentFiber
下的第一個孩子
看着比較煩瑣,然則實際上做的就是構建鏈表和 diff
孩子的歷程,這個函數有許多優化的空間,運用 key
今後,在這裡能進步許多的機能,為了簡樸,我並沒有對 key
舉行操縱,今後的 Luy
版本肯定會的。
completeWork: 網絡 effectTag
// 最先遍歷
function performUnitOfWork(workInProgress) {
const nextChild = beginWork(workInProgress)
if (nextChild) return nextChild
// 沒有 nextChild, 我們看看這個節點有無 sibling
let current = workInProgress
while (current) {
//網絡當前節點的effect,然後向上通報
completeWork(current)
if (current.sibling) return current.sibling
//沒有 sibling,回到這個節點的父親,看看有無sibling
current = current.return
}
}
//網絡有 effecttag 的 fiber
function completeWork(currentFiber) {
if (currentFiber.tag === tag.classComponent) {
// 用於回溯最高點的 root
currentFiber.stateNode._internalfiber = currentFiber
}
if (currentFiber.return) {
const currentEffect = currentFiber.effects || [] //網絡當前節點的 effect list
const currentEffectTag = currentFiber.effectTag ? [currentFiber] : []
const parentEffects = currentFiber.return.effects || []
currentFiber.return.effects = parentEffects.concat(currentEffect, currentEffectTag)
} else {
// 抵達最頂端了
pendingCommit = currentFiber
}
}
這個函數做了兩件事,第一件事變就是網絡當前 currentFiber
的 effectTag
,將其 append
到父 Fiber
的 effectlist
中去,經由歷程輪迴一層一層往上,終究抵達頂端 currentFiber.return === void 666
的時刻,證實我們抵達了 root
,此時我們已把一切的 effect
網絡到了頂端的 currentFiber.effect
上,並把它賦值給 pendingCommit
,進入 commitAllWork
階段。
第三步:commitAllWork
終究,我們已經由歷程不停不停的挪用 requestIdleCallback
和 大輪迴,將我們的一切變動都找出來放在了 workInProgress tree
里,我們接下來就要做末了一步:將一切的變動一次性的變動到實在 DOM
中,注重,這個階段里我們不再運轉建立 DOM
和 render
,因而,雖然我們一次性變動一切的 DOM
,然則機能來講並非太差。
function commitAllwork(topFiber) {
topFiber.effects.forEach(f => {
commitWork(f)
})
topFiber.stateNode._rootContainerFiber = topFiber
topFiber.effects = []
nextUnitOfWork = null
pendingCommit = null
}
我們直接拿到 TopFiber
中的 effects list
,遍歷,將變動悉數打到 DOM
中去,然後我們將全局變量清算清潔。
function commitWork(effectFiber) {
if (effectFiber.tag === tag.HostRoot) {
// 代表 root 節點沒什麼必要操縱
return
}
// 拿到parent的緣由是,我們要將元素插進去的點,插在父親的下面
let domParentFiber = effectFiber.return
while (domParentFiber.tag === tag.classComponent || domParentFiber.tag === tag.FunctionalComponent) {
// 假如是 class 就直接跳過,由於 class 範例的fiber.stateNode 是其自身實例
domParentFiber = domParentFiber.return
}
//拿到父親的實在 DOM
const domParent = domParentFiber.stateNode
if (effectFiber.effectTag === PLACEMENT) {
if (effectFiber.tag === tag.HostComponent || effectFiber.tag === tag.HostText) {
//經由歷程 tag 搜檢是不是是實在的節點
domParent.appendChild(effectFiber.stateNode)
}
// 其他狀況
} else if (effectFiber.effectTag == UPDATE) {
// 更新邏輯 只能是沒完成
} else if (effectFiber.effectTag == DELETION) {
//刪除過剩的舊節點
commitDeletion(effectFiber, domParent)
}
}
function commitDeletion(fiber, domParent) {
let node = fiber
while (true) {
if (node.tag == tag.classComponent) {
node = node.child
continue
}
domParent.removeChild(node.stateNode)
while (node != fiber && !node.sibling) {
node = node.return
}
if (node == fiber) {
return
}
node = node.sibling
}
}
這一部份代碼是最好明白的了,就是做的是刪除和插進去或許更新 DOM
的操縱,值得注重的是,刪除操縱照舊運用的鏈表操縱。
末了來一段測試代碼:
import React from './Luy/index'
import { Component } from './component'
import { render } from './vdom'
class App extends Component {
state = {
info: true
}
constructor(props) {
super(props)
setTimeout(() => {
this.setState({
info: !this.state.info
})
}, 1000)
}
render() {
return (
<div>
<span>hello</span>
<span>luy</span>
<div>{this.state.info ? 'imasync' : 'iminfo'}</div>
</div>
)
}
}
render(<App />, document.getElementById('root'))
我們來看看動圖吧!當節點 mount
今後,過了 1 秒,就會更新,我們簡樸的更新就到此結束了
再看以下挪用棧,我們的 requestIdleCallback
函數已準確的運轉了。
假如你想下載代碼親身體驗,可以到 Luy 堆棧中:
git clone https://github.com/Foveluy/Luy.git
cd Luy
npm i --save-dev
npm run start
如今我能找到的一切材料都放在堆棧中:材料
回憶本文幾個主要的點
一最先我們就運用了一個數組來紀錄 update
的信息,經由歷程挪用 requestIdleCallback
來將更新一個一個的取出來,大部份時刻行列里只要一個。
取出來今後,運用從左向右遍歷的體式格局,用鏈錶鏈接一個一個的 Fiber
,並做 diff
和建立,末了一次性的 patch
到實在 DOM
中去。
如今 react 的架構已變得極為龐雜,而本文也只是將 React 的團體架構通篇流程形貌了一遍,內里的細節照舊值得我們的窮究,比方,怎樣通報 context
,怎樣完成 ref
,怎樣完成毛病邊境處置懲罰,聲明周期的處置懲罰,這些都是很大的話題,在接下去的文章里,我會一步一步的將這些關聯講清楚。
末了,感謝支撐我的迷你框架項目:Luy ,如今正在向 Fiber
升級!假如你喜好,請給我一點 star🌟 示意勉勵!感謝
假如有什麼題目,可以到場我們的進修 QQ 群: 370262116
,群里險些一切的迷你 React
作者都在了,包含 anu
作者司徒正美, omi
作者,我等,一同來進修吧!