Appearance
首屏初始化
- 首屏初始化过程中,生成
FiberRootNode
,其current
指针指向应用根节点(tag = 3),根节点 下面是App
的FiberNode
- 首屏渲染启动,生成
workInProgress(wip)
,createWorkInProgress(root.current)
,参数是上面 1 生成的 根节点FiberNode
js
function createWorkInProgress(current, pendingProps){
let workInProgress = current.alternate // 获取 根节点 的alternate
if (workInProgress === null) {
// 首屏渲染为空的逻辑
// 根据当前根节点,创建 wip
workInProgress = createFiber(current.tag);
// wip,createFiber返回一个要渲染的 FiberNode
// ...一些 current属性复制给 wip
} else {
// 更新分支逻辑
// 因为更新时有缓存之前的 FiberNode, 直接复用之前node的属性,不重新创建
}
workInProgress.lanes = current.lanes;
workInProgress.child = current.child;
workInProgress.memoizedProps = current.memoizedProps;
workInProgress.memoizedState = current.memoizedState;
workInProgress.updateQueue = current.updateQueue;
// ... 将current的属性重新赋值到wip
return workInProgress;
}
- 返回的 wip(根节点),通过
performUnitOfWork
进行 子节点 的生成,并更新wip
js
function performUnitOfWork(unitOfWork) {
//... unitOfWork 其实是当前 wip
let next
next = beginWork$1(current, unitOfWork, subtreeRenderLanes);
// 返回 childFiberNode ...
workInProgress = next
// 最后更新 wip,外层 workLoopSync 循环继续更新子节点
}
- 生成 所有子节点 之后进行
commit
进行渲染到实际 DOM - 渲染之后,
FiberRootNode
的current
指针,指向最新生成的 Fiber 树
更新阶段的递
第一次更新
触发更新的时候,是从 根节点(rootFiber)开始往下更新
在 createWorkInProgress
逻辑,执行 else
分支
然后 重复执行 createWorkInProgress
流程,更新 wip
注意:更新过程中,新创建的
wip
会沿用原current
的属性字段(如 child) 后续更新中,child 也会创建新的wip
创建完所有 wip
,commit
渲染,并更新 current
指针
小结
第一次更新之后,会成功创建出两颗 FiberNode
树
第二次更新
第二次更新过程,每次
createWorkInProgress
创建wip
,其current
都有alternate
,实现双缓存
- 第二次更新过程中,
FiberNode
都有对应的current
,生成wip
执行beginwork
,执行不同逻辑
js
// 更新 rootFiber 过程
function beginWork(current, workInProgress) {
// wip 对比 current的属性没有变化,复用节点
didReceiveUpdate = false; // 标记该 FiberNode 是否有更新
return attemptEarlyBailoutIfNoScheduledUpdate(current, workInProgress, renderLanes);
// App 节点逻辑
didReceiveUpdate = false;
switch (workInProgress.tag) {
case FunctionComponent:
{
// 也就是传入 App组件 构造函数
var Component = workInProgress.type;
var unresolvedProps = workInProgress.pendingProps;
var resolvedProps = workInProgress.elementType === Component ? unresolvedProps : resolveDefaultProps(Component, unresolvedProps);
// 更新函数式组件
return updateFunctionComponent(current, workInProgress, Component, resolvedProps, renderLanes);
}
}
}
// 内部调用
function bailoutOnAlreadyFinishedWork(current, workInProgress) {
//...
cloneChildFibers(current, workInProgress);
return workInProgress.child
// 返回 wip 子节点到 performUnitOfWork 作为 next
}
// 复用节点优化逻辑,复制所有子节点
function cloneChildFibers(current, workInProgress) {
if (workInProgress.child === null) {
return;
}
var currentChild = workInProgress.child;
// 根据子节点创建FiberNode
var newChild = createWorkInProgress(currentChild, currentChild.pendingProps)
// 更新wip 的child
workInProgress.child = newChild;
newChild.return = workInProgress;
// 子兄弟节点复制
while (currentChild.sibling !== null) {
currentChild = currentChild.sibling;
newChild = newChild.sibling = createWorkInProgress(currentChild, currentChild.pendingProps);
newChild.return = workInProgress;
}
newChild.sibling = null;
}
rootFiber
会复用原有的属性,执行bailoutOnAlreadyFinishedWork
返回子节点App- App 节点,通过
updateFunctionComponent
更新节点
js
function updateFunctionComponent(current, wip, Component, nextProps) {
// ...
// 内部执行 Component 构造函数,返回jsx对象
nextChildren = renderWithHooks(current, workInProgress, Component, nextProps, context, renderLanes);
// ...
workInProgress.flags |= PerformedWork;
// 子节点child更新
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
return workInProgress.child;// 返回上层进行下一个更新
}
// 调用构造函数
function renderWithHooks(current, wip, Component, nextProps, secondArg) {
//...
const children = Component(props, secondArg); // 返回App 的 jsx 对象
//...
return children
}
function reconcileChildren(current, wip, nextChildren) {
// 由于有current,reconcileChildFibers 标记 flags 为true
reconcileChildFibers(wip, current.child, nextChildren)
// 1. reconcileChildren
// 2. reconcileSingleElement
// 3. useFiber
// 4. createWorkInProgress
// 5. placeSingleChild
// 双缓存,返回 child.alternate
}
function reconcileSingleElement(returnFiber, currentFirstChild, element) {
// ...
while (child !== null) {
const _existing = useFiber(child, element.props);
// 内部调用createWorkInProgress
_existing.return = returnFiber;
return _existing;
}
}
function placeSingleChild(newFiber) {
// 只有是 current没有的 FiberNode,只在当前wip下新增的会加 flags
if (shouldTrackSideEffects && newFiber.alternate === null) {
newFiber.flags |= Placement;
}
return newFiber;
}
// 返回 App 下的子节点
小结
- 第二次更新过程中,由于双缓存机制,每次
wip
都是返回current
的alternate
,没有重新生成FiberNode
- 根据
child
节点是单个还是 数组,调用不同方法,最终都是返回alternaete
作为新的wip
进行下一个 循环更新