最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • react原理:completeWork阶段

    正文概述 掘金(争霸爱好者)   2021-07-31   771

    这篇文章讲一下render阶段的第二部分:completeUnitOfWork

    reactrender阶段,会按照先序遍历的顺序构建fiber树,当workInProgress指针来到一个叶子节点时,就会执行completeUnitOfWork方法,这个方法会修改workInProgress指针,让其指向先序遍历中的下一个节点,下面,就来介绍一下completeUnitOfWork完成的工作。

    completeUnitOfWork主要做了三件事:dom节点的创建和更新,effectList的收集和错误的处理。关于错误处理的内容,笔者也没有深入了解过,这篇文章就介绍一下前两部分内容:dom节点的创建和更新。

    Dom节点的创建和更新

    这部分的内容在completeWork函数中。completeWork会根据不同的组件类型进入不同的逻辑。由于completeWork主要会做dom相关的操作,因此我们可以主要看HostComponent部分的代码。

    popHostContext(workInProgress);
    var rootContainerInstance = getRootHostContainer();
    var type = workInProgress.type;
    
    if (current !== null && workInProgress.stateNode != null) {
      // 节点更新
      updateHostComponent$1(current, workInProgress, type, newProps, rootContainerInstance);
    
      if (current.ref !== workInProgress.ref) {
        markRef$1(workInProgress);
      }
    } else {
      // 新增dom节点
      // ...
      var currentHostContext = getHostContext();
    
      var _wasHydrated = popHydrationState(workInProgress);
    
      if (_wasHydrated) {
        // 服务端渲染相关
      } else {
        var instance = createInstance(type, newProps, rootContainerInstance, currentHostContext, workInProgress);
        appendAllChildren(instance, workInProgress, false, false);
        workInProgress.stateNode = instance;
    
        if (finalizeInitialChildren(instance, type, newProps, rootContainerInstance)) {
          markUpdate(workInProgress);
        }
      }
    
      if (workInProgress.ref !== null) {
        markRef$1(workInProgress);
      }
    }
    
    return null;
    

    可以看到,对于HostComponent的处理分为dom更新和dom创建两个过程。针对更新,主要是dom属性的更新,对于新增的dom节点,会先创建一个dom节点,之后将该节点挂载到fiberstateNode属性上,之后插入该节点(appendAllChildren)。由于在fiber架构中,组件树和dom并不是对应的,比如函数组件和类组件不存在对应的dom节点,因此appendAllChildren这部分的逻辑比较繁琐,这里就不展开讲解了,有兴趣的可以看一下@Axizs大佬的文章。

    effectList收集

    在完成dom相关的更新之后,回到completeUnitOfWork中,会开始effectList链的收集。effectList链的作用就是将有副作用的fiber节点收集为一条链表,这样在commit阶段中,就可以根据effectList来进行操作,不需要再遍历一次fiber树。下面看一下这部分代码

    // returnFiber就是当前正在处理的workInProgress节点的父节点
    // completedWork就是正在处理的fiber节点
    if (returnFiber.firstEffect === null) {
      returnFiber.firstEffect = completedWork.firstEffect;
    }
    
    // 将completedWork身上的effectList拼接到returnFiber的后面
    if (completedWork.lastEffect !== null) {
      if (returnFiber.lastEffect !== null) {
        returnFiber.lastEffect.nextEffect = completedWork.firstEffect;
      }
      returnFiber.lastEffect = completedWork.lastEffect;
    }
    
    var flags = completedWork.flags;
    
    // 将completedWork自己拼接到returnFiber的后面
    if (flags > PerformedWork) {
      if (returnFiber.lastEffect !== null) {
        returnFiber.lastEffect.nextEffect = completedWork;
      } else {
        returnFiber.firstEffect = completedWork;
      }
    
      returnFiber.lastEffect = completedWork;
    }
    

    也就是说,effectList的顺序是从层次最深的子节点开始向上收集,层次最深的fiber节点位于effectList链表的头部。最终,workInProgress树的rootFiber节点会得到完整的effectList。不是很清楚的同学可以看一下b站的这个视频。

    注意点

    关于completework部分有一些需要注意的地方:

    completeWork中创建了新的dom节点,但是此时,这些新的dom节点并没有被插入到dom树中,比如看下面的例子

    import { useState, useCallback } from 'react'
    
    const App = () => {
      const [visible, change] = useState(false)
      const changeVisible = useCallback(() => change((pre) => !pre), [change])
      return (
        <div>
          {visible && (
            <div>
              <h1>新的div</h1>
            </div>
          )}
          <p>老的内容</p>
          <button onClick={changeVisible}>click</button>
        </div>
      )
    }
    
    export default App
    

    点击按钮,visible变为true,此时,在completework中,h1被添加到了div中,但是div并没有被添加到dom树中。相反,这个div会被打上placementtag,从而被收集到effectList中,在之后的commit阶段中,这个div会被添加到真实的dom树中,从而实现页面的更新。

    completeWork之后,render阶段结束,进入commit阶段,之后的文章会为大家讲解。


    起源地下载网 » react原理:completeWork阶段

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元