Fiber
react15在render阶段的reconcile是不可打断的,这会在进行大量dom的reconcile时产生卡顿,因为浏览器所有的时间都交给了js执行,并且js的执行时单线程。为此react16之后就有了scheduler进行时间片的调度,给每个task一定的时间,如果在这个时间内没执行完,也要交出执行权给浏览器进行绘制和重排,所以异步可中断的更新需要一定的数据结构在内存中来保存dom的信息,这个数据结构就是Fiber(虚拟dom)。
Fiber的数据结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| function FiberNode( tag: WorkTag, pendingProps: mixed, key: null | string, mode: TypeOfMode, ) { this.tag = tag; this.key = key; this.elementType = null; this.type = null; this.stateNode = null;
this.return = null; this.child = null; this.sibling = null; this.index = 0;
this.ref = null;
this.pendingProps = pendingProps; this.memoizedProps = null; this.updateQueue = null; this.memoizedState = null; this.dependencies = null;
this.mode = mode;
this.effectTag = NoEffect; this.nextEffect = null; this.firstEffect = null; this.lastEffect = null;
this.lanes = NoLanes; this.childLanes = NoLanes;
this.alternate = null; }
|
1 2 3 4 5 6
| createFiberRoot FiberRootNode: 指整个应用的根节点,只存在一个 createHostRootFiber rootFiber: ReactDOM.render或者ReactDOM.unstable_createRoot创建出来的应用的节点,可以存在多个。
current Fiber workInProgress Fiber 通过 alternate 指针连接
|
Fiber 双缓存
现在我们知道了Fiber可以保存真实的dom,真实dom对应在内存中的Fiber节点会形成Fiber树,这颗Fiber树在react中叫current Fiber,也就是当前dom树对应的Fiber树,而正在构建Fiber树叫workInProgress Fiber,这两颗树的节点通过alternate相连.
1 2 3 4 5 6 7 8 9 10
| function App() { return ( <div> xiao <p>chen</p> </div> ) }
ReactDOM.render(<App />, document.getElementById("root"));
|
mount
createFiberRoot
刚开始只创建了fiberRoot和rootFiber两个节点
1 2 3 4 5
| var root = new FiberRootNode() var uninitializedFiber = createHostRootFiber => createFiber => new FiberNode() root.current = uninitializedFiber; uninitializedFiber.stateNode = root; return root;
|
接下来构建字节点, 分别是以下两个方法
reconcileChildFibers
和 createWorkInProgress
createWorkInProgress (current, pendingProps)
然后根据jsx创建workInProgress Fiber
current
当前 Fiber
current.type
等于 3 表示当前节点为rootFiber节点
1 2 3 4 5 6 7 8 9 10 11
| workInProgress = current.alternate
if(workInProgress === null) { workInProgress = createFiber(current.tag, pendingProps, current.key, current.mode) workInProgress.elementType = current.elementType workInProgress.type = current.type workInProgress.stateNode = current.stateNode }
workInProgress.alternate = current current.alternate = workInProgress
|
接下来进入到reconcileChildrenFibers
, 构建rootFiber
的子节点
reconcileChildrenFibers
1 2 3 4 5 6 7
| reconcileChildren(current, workInProgress, nextChildren, renderLanes) { if (current === null) { workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderLanes) } else { workInProgress.child = reconcileChildFibers(workInProgress, current.child, nextChildren, renderLanes) } }
|
深度优先遍历
根据jsx(newChild)构建相应节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| reconcileChildFibers(returnFiber, currentFirstChild, newChild, lanes) { reconcileSingleElement(returnFiber, currentFirstChild, element, lanes) { var created = createFiberFromElement(element, mode, lanes) { var fiber = createFiberFromTypeAndProps(type, key, pendingProps, owner, mode, lanes) { var fiberTag = IndeterminateComponent; var fiber = createFiber(fiberTag, pendingProps, key, mode); fiber.elementType = type; fiber.type = resolvedType; fiber.lanes = lanes; return fiber; } return fiber; } created.return = returnFiber; return created; } }
|
把workInProgress Fiber切换成current Fiber
1 2 3 4 5
| var finisedhWork = root.current.alternate; root.finishedWork = finishedWork; root.finishedLanes = lanes; commitRoot(root);
|
commit 阶段
1 2
| root.current = finishedWork
|
update
createWorkInProgress(current, pendingProps)
current.type
为 3, 表明当前节点时rootFiber
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| var workInProgress = current.alternate;
if(workInProgress === null) { workInProgress = createFiber(current.tag, pendingProps, current.key, current.mode); ... workInProgress.alternate = current; current.alternate = workInProgress; }
workInProgress.childLanes = current.childLanes; workInProgress.lanes = current.lanes; workInProgress.child = current.child;
|
然后进入 reconcileChildFibers 深度遍历子节点
reconcileChildFibers
深度遍历
再次执行切换过程
commitRootImpl
切换 current