react源码debugger-各种fiber的completeWork阶段
Posted coderlin_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了react源码debugger-各种fiber的completeWork阶段相关的知识,希望对你有一定的参考价值。
原生div p标签等的bieginWork
class DD extends Component
render()
return <div>123</div>;
上面调式了DD类组件的fiber,接着看类组件返回的子fiber,div标签的fiber是怎样的。
对于原生html的fiber,fiber.tag是5
export const HostComponent = 5; //原生标签
hostComponent的fiber
alternate: null
child: null
elementType: "div"
key: null
lanes: 16
memoizedState: null
mode: 1
pendingProps: children: '123'
ref: null
return: FiberNode tag: 1, key: null, stateNode: DD, elementType: ƒ, type: ƒ, …
sibling: null
stateNode: null
tag: 5
type: "div"
updateQueue: null
对于单一文本子节点的Fiber
,React
会特殊处理。比如div的子节点123,他没有beignWOrk和completeWork
在beginWOrk,对于HostComponent,会调用 updateHostComponent
方法
对于updateHostComponent,
function updateHostComponent(current, workInprogress, renderLanes)
const type = workInProgress.type; // div
const nextProps = workInProgress.pendingProps; //更新后的props
const prevProps = current !== null ? current.memoizedProps : null; //当前的Props
let nextChildren = nextProps.children; // <div>123</div>中的123 children: 123
//调用shouldSetTextContent优化
if (isDirectTextChild)
nextChildren = null; // 设为Null,不创建子fiber
else if (prevProps !== null && shouldSetTextContent(type, prevProps))
workInProgress.flags |= ContentReset;
//创建子fiber
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
return workInProgress.child;
他会调用shouldSetTextContent
判断是否为子节点创建fiber,他的判断是
function shouldSetTextContent(type,props)
return n type === 'textarea' || type === 'noscript' || typeof props.children === 'string' || typeof props.children === 'number'....
可以看到,对于像<div>123</div>
的123是不会创建子fiber的。
nextChildren置为了null,调用reconcileChildren
的时候,因为nextChildren === null,所以他不会创建子fiber,所以div Fiber返回的就是null。
那么就到了归阶段的逻辑了。
每个fiber的归阶段
我们知道performUnitOfWork
是每个fiber开始工作的函数。
function performUnitOfWork(unitOfWork: Fiber): void
const current = unitOfWork.alternate; // 获取调度的fiber在当前页面上对应的fiber
let next;
...
// 调用beginWork,返回下一个工作节点
next = beginWork(current, unitOfWork, subtreeRenderLanes);
unitOfWork.memoizedProps = unitOfWork.pendingProps;
if (next === null)
// 进入归阶段
completeUnitOfWork(unitOfWork);
else
workInProgress = next;
ReactCurrentOwner.current = null;
如上,我们知道div Fiber在beginWork的reconcilerChildren的时候,因为优化,所以没有创建fiber,那么返回的就是null,所以next = null,那么就进入了归阶段。
我们的demo是这样的
const App: React.FC = () =>
return <DD />;
;
class DD extends Component
render()
return <div>123</div>;
我们了解了一共四种主要的fiber的beginWork阶段,hostRoot,也就是rootFiber,以及函数组件(mount的时候函数组件统统是IndeterminateComponent,只有当执行过后才会将tag改为FunctionComponent),以及类组件DD,和原生标签div等主要的tag的fiber的递阶段。
按照render阶段的递归逻辑。
beginWork执行阶段是
rootFiber beginWork
App beginWork
DD beginWork
div beginWork
completeWork的执行阶段则是
div beginWokr
DD beginWork
App beginWork
rootFiber beginWork
归阶段遵循一个原则。
-
当所有的子fiber完成归阶段的时候,就轮到自己完成归阶段了。
-
当自己完成归阶段的时候,就要判断有没有兄弟节点,有的话返回兄弟节点去执行beginWork,等兄弟节点执行完beginWork的时候,还会进来归阶段,从第一个原则继续开始判断。
-
当自己完成归阶段,并且没有兄弟节点,那么对于父节点来说,所有子fiber就完成了归阶段,那么就轮到父fiber执行completeWork。以此类推,直到rootFiber也完成completeWork,那么,render阶段到此结束
div fiber completeUnitOfWork
归阶段的入口是completeUnitOfWork
,来看下这个函数大概做了什么。
function completeUnitOfWork(unitOfWork)
let completedWork = unitOfWork;
// do while循环执行completeWork
do
...
next = completeWork(current, completedWork, subtreeRenderLanes);
// 判断依据
const siblingFiber = completedWork.sibling;
if (siblingFiber !== null)
// 如果有兄弟节点,就退出completWOrk,执行兄弟节点的beginWork
workInProgress = siblingFiber;
return;
//否则返回returnFiber,继续执行completeWork
completedWork = returnFiber;
workInProgress = completedWork;
while(completedWork !== null)
如上,主要就是do while循环执行completeWork,有兄弟节点,就直接退出当前循环,执行兄弟节点的beginWOrk。没有就表示所有子fiber执行完毕,那么就轮到父fiber执行completeWork函数。
completeWork
对于completeWork,主要就是根据不同的tag执行不同的函数。按照顺序,第一个执行completeWOrk的就是div fiber。
对于div fiber,completeWork会走HostComponent。看看大概思路:
- 创建dom,挂载到fiber.stateNode
- 调用appendAllChildren,将自己子fiber的stateNode插入到创建的dom上
- 如果有ref,处理ref
// 获取挂载的element 即 <div id="root"/>的元素
const rootContainerInstance = getRootHostContainer();
if (current !== null && workInProgress.stateNode != null)
// update阶段并且有dom元素
..
else
..
//创建Dom
const instance = createInstance(
type,
newProps,
rootContainerInstance,
currentHostContext,
workInProgress,
);
//关键函数,将自己儿子的dom插到自己身上
appendAllChildren(instance, workInProgress, false, false);
//将dom挂载在fiber.stateNode
workInProgress.stateNode = instance;
if (workInProgress.ref !== null)
// 有ref元素
// If there is a ref on a host node we need to schedule a callback
markRef(workInProgress);
return null
- 对于createInstance,它会根据type创建对应的dom元素。
- 对于appendAllChildren,他会while循环,将子fiber的dom插入到当前dom上
- markRef会在fiber.tags上赋值一个标记。在commit阶段会处理
- 返回null,结束div fiber的归阶段。
类组件的completeWork
当div fiber结束completeWork的时候,
div fiber没有兄弟节点,所以returnFiber也就是类DDFiber继续while循环。
…未完待续
以上是关于react源码debugger-各种fiber的completeWork阶段的主要内容,如果未能解决你的问题,请参考以下文章
react源码debugger-各个hooks的逻辑实现(useState和useEffect)
react源码debugger-各个hooks的逻辑实现(useState和useEffect)