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循环。

class DD extends Component {
  render() {
    return <div>123</div>;
  }
}

对于类组件,

 case ClassComponent: {
      //类组件执行这个
      const Component = workInProgress.type; // DD类
      if (isLegacyContextProvider(Component)) {
        popLegacyContext(workInProgress);
      }
      // 跟函数组件一样
      bubbleProperties(workInProgress);
      return null;
    }

在这里插入图片描述

对于类组件,更新了儿子的优先级等等。

类组件执行完completeWork之后,因为DD Fiber没有兄弟节点,所以直接返回App Fiber也就是函数组件。由函数组件执行completeWork。而对于函数组件,

case IndeterminateComponent:
    case LazyComponent:
    case SimpleMemoComponent:
    case FunctionComponent:
    case ForwardRef:
    case Fragment:
    case Mode:
    case Profiler:
    case ContextConsumer:
    case MemoComponent:
      // 函数组件等执行这个
      bubbleProperties(workInProgress);
      return null;

执行的也是bubbleProperties这个函数,与类组件一样。

当App Fiber完成completeWork的时候。因为App Fiber没有兄弟节点,所以返回了RootFiber。

RootFiber的completeWork

在这里插入图片描述

updateHostContainer在首次执行的时候为空函数。bubbleProperites是跟类组件函数组件一样的执行。

当RootFiber执行完归阶段的时候

在这里插入图片描述

RootFiber没有兄弟节点,也没有父亲节点。所以跳出while循环。

当completeUnitOfWork执行完毕的时候
在这里插入图片描述

performUnitOfWork执行完毕。而performUnitOfWork是render阶段的入口,至此render阶段正式完成,workInProgress是null。
在这里插入图片描述

当下次Schedule执行performConcurrentWorkOnRoot的时候,最终调用workLoopConcurrent的时候,不用进循环了。

在这里插入图片描述

因为workInprogress为null,重置全局变量,然后返回一个退出状态。
renderRootConcurrent每次执行一次render阶段的函数之后,都会返回一个状态,用来给performConcurrentWorkOnRoot判断是否应该执行commit阶段。
在这里插入图片描述

状态一共有五个,推出时候的状态为RootCompleted,5表示已经完成。平常在执行的时候就是1,RootInProgress,表示在进行中。

然后看performConcurrentWorkOnRoot的逻辑、

let exitStatus = shouldTimeSlice
    ? renderRootConcurrent(root, lanes)
    : renderRootSync(root, lanes);

将状态保存,然后会判断

if(exitStatus !== RootInProgress){
    if(exitStatus === RootErrored){}....
    else {
        //exitStatus === RootCompleted的时候
        const finishedWork: Fiber = (root.current.alternate: any); // root是FiberRoot,finishedWork就是已经完成了的workInprogress fiber树的rootFiber
        root.finishedWork = finishedWork; // 挂载到FiberRoot上
         //完成了render阶段之后,开启commit阶段
     	 finishConcurrentRender(root, exitStatus, lanes);
    }
}

最关键就是这个finishConcurrentRender函数,他会开启commit阶段。至此,Render阶段结束,准备开启Commit阶段。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coderlin_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值