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;//key属性
this.elementType = null;//元素类型
this.type = null;//func或者class
this.stateNode = null;//真实dom节点

//连接成fiber树
this.return = null;//指向父节点
this.child = null;//指向child
this.sibling = null;//指向兄弟节点
this.index = 0;

this.ref = null;

//用来计算state
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = null;

this.mode = mode;

//effect相关
this.effectTag = NoEffect;
this.nextEffect = null;
this.firstEffect = null;
this.lastEffect = null;

//优先级相关的属性
this.lanes = NoLanes;
this.childLanes = NoLanes;

//current和workInProgress的指针
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()  //  和上面的FiberNode比较类似,增加了hydrate,expirationTimes(构建时间)之类的属性
var uninitializedFiber = createHostRootFiber => createFiber => new FiberNode()
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;
return root;

接下来构建字节点, 分别是以下两个方法

reconcileChildFiberscreateWorkInProgress

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) {
// returnFiber 返回的Fiber
// returnFiber currentFirstChild 第一次构建为null
// newChild jsx
// 接下来进入到
reconcileSingleElement(returnFiber, currentFirstChild, element, lanes) {
// 接着调用createFiberFromElement
// 从jsx对象创建Fiber
var created = createFiberFromElement(element, mode, lanes) {
var fiber = createFiberFromTypeAndProps(type, key, pendingProps, owner, mode, lanes) {
// IndeterminateComponent = 2; Before we know whether it is function or class
var fiberTag = IndeterminateComponent;
var fiber = createFiber(fiberTag, pendingProps, key, mode);
fiber.elementType = type;
fiber.type = resolvedType;
fiber.lanes = lanes;
return fiber;
}
return fiber;
}
// * 构造好的 fiber 的 return 指向父节点
created.return = returnFiber;
return created;
}
}

把workInProgress Fiber切换成current Fiber

1
2
3
4
5
// performSyncWorkOnRoot
var finisedhWork = root.current.alternate;
root.finishedWork = finishedWork;
root.finishedLanes = lanes;
commitRoot(root);

commit 阶段

1
2
// commitRootImpl
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;
// workInProgress.tag = 3 克隆或者服用而来
// 如果不存在 节点, createFiber
if(workInProgress === null) {
workInProgress = createFiber(current.tag, pendingProps, current.key, current.mode);
...
workInProgress.alternate = current;
current.alternate = workInProgress;
}

// 把 current 属性 复制到 WorkInProgress
workInProgress.childLanes = current.childLanes;
workInProgress.lanes = current.lanes;
workInProgress.child = current.child;
// ...


// 并且 alternate 指针互相连接

然后进入 reconcileChildFibers 深度遍历子节点

reconcileChildFibers

深度遍历

再次执行切换过程

commitRootImpl

切换 current