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

对于单一文本子节点的FiberReact会特殊处理。比如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。看看大概思路:

  1. 创建dom,挂载到fiber.stateNode
  2. 调用appendAllChildren,将自己子fiber的stateNode插入到创建的dom上
  3. 如果有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-commit阶段的完成

react源码debugger-commit阶段的完成

react源码debugger-各个hooks的逻辑实现(useState和useEffect)

react源码debugger-各个hooks的逻辑实现(useState和useEffect)

react源码解析-debugger阶段-类组件mount阶段的beginWork