react源码阅读3 update与updateQueue

react-dom后续updateContainer部分。阅读React包的源码版本为16.8.6

  在上一章节中我们看到了react-domrender函数的逻辑是给传入的React组件创建了一个fiberRoot对象,用于标识它是整个应用的起点,上面拥有很多应用更新相关的表示符。然后创建对应的fiberfiberRoot节点,fiber对象是每一个ReactElement都拥有的节点,它标识了更新时间的一些信息,props和state的一些信息,以及相关联的节点信息。ReactElement彼此是通过一个单向链表的数据结构联系在一起的。

  这一章我们接着legacyRenderSubtreeIntoContainer函数创建完fiber相关对象的部分,查看接下来updateContainer相关的逻辑。我们先回顾一下这部分的代码。

function legacyRenderSubtreeIntoContainer(
  parentComponent: ?React$Component<any, any>,
  children: ReactNodeList,
  container: DOMContainer,
  // 是否复用dom节点,服务端渲染调用
  forceHydrate: boolean,
  callback: ?Function,
) {
   
  // ...省略创建fiber节点相关部分逻辑
  // 初次使用render不存在root节点
  if (!root) {
   
    // ...省略创建fiber节点相关部分逻辑
    if (typeof callback === 'function') {
   
      const originalCallback = callback;
      callback = function() {
   
        const instance = getPublicRootInstance(fiberRoot);
        originalCallback.call(instance);
      };
    }
    // Initial mount should not be batched.
    unbatchedUpdates(() => {
   
      updateContainer(children, fiberRoot, parentComponent, callback);
    });
  } else {
   
    fiberRoot = root._internalRoot;
    // 有无callback 逻辑同上
    if (typeof callback === 'function') {
   
      const originalCallback = callback;
      callback = function() {
   
        const instance = getPublicRootInstance(fiberRoot);
        originalCallback.call(instance);
      };
    }
    // Update
    updateContainer(children, fiberRoot, parentComponent, callback);
  }
  return getPublicRootInstance(fiberRoot);
}

  我们先看callback部分的处理,在render函数中callback是传入的第三个参数,根据react文档,该回调将在组件被渲染或更新之后被执行,并且在非箭头函数的情况下,该回调的this指向render渲染的那个组件。我们先来回顾下这个入参的使用方式。

const instance = render(
  <Hello text="123" />,
  document.getElementById("page"),
  function () {
    console.log(this) }
);

console.log(instance);

/*
this === instance === Hello
Hello {
  isMounted: (...)
  replaceState: (...)
  props: {text: "123"}
  context: {}
  refs: {}
  updater: {isMounted: ƒ, enqueueSetState: ƒ, enqueueReplaceState: ƒ, enqueueForceUpdate: ƒ}
  _reactInternalFiber: FiberNode {tag: 1, key: null, stateNode: Hello, elementType: ƒ, type: ƒ, …}
  _reactInternalInstance: {_processChildContext: ƒ}
  state: null
  __proto__: Component
}
*/

  在上述代码中使用render函数时,传入了一个匿名函数作为render的第三个入参,并打印了this,然后将render函数的返回值赋予了instance变量并打印出来。我们可以看到,输出的是一个对象信息,其实使用过react测试相关诸如react-test-renderer等框架的,应该对这个instance比较熟悉。它标志了一个由fiberRoot开始的完整的组件信息。

  现在回到源码,我们可以看到我们在调用render时候,callback的this和render返回的组件instance信息都是由getPublicRootInstance创建的。react将我们传入的callback赋值给了变量originalCallback,然后声明一个新的callback,新的callback创建了一个instance,然后用calloriginalCallback的this指向它,把它传入到了updateContainercallback参数中。

  至于getPublicRootInstance如何创建一个Instance的细节代码与主流程牵扯不大,这边就跳过。只要知道该函数根据fiberRoot提供了一个Instance信息对象即可。接着我们可以看到,无论是否是初次使用render函数(初次调用render函数不存在root节点),legacyRenderSubtreeIntoContainer都调用了updateContainer方法,区别就是初次使用render的时候,updateContainer是在unbatchedUpdates方法回调中使用的。unbatchedUpdates做的事情实际就是在render初次调用的时候,不用去批量更新updateContainer,这个函数做的事情仅仅是改变了几个标志符,然后立即更新了CallbackQueue,我们也略过这部分逻辑,重点来看一下updateContainer相关的部分。

function updateContainer(
  element: ReactNodeList,
  container: OpaqueRoot, // root
  parentComponent: ?React$Component<any, any>, // 根节点是null
  callback: ?Function,
): ExpirationTime {
   
  // fiberRoot的current为fiberRoot的fiber对象
  const current = container.current;
  // 获得当前时间到js加载完时间的时间差值
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值