资讯专栏INFORMATION COLUMN

ReactRoot与ReactWork源码分析

macg0406 / 750人阅读

摘要:在中调用获得了的实例,然后调用其中的回调函数中调用了方法。这个类主要介绍其构造函数和方法,构造函数是时调用的,方法是的回调函数中使用的。这个将在下一篇分析。另外,方法是在的回调函数中调用的,也是一个参与后面调度的关键。

在ReactDOM.render源码解析-1中介绍了第一次render的基本过程的一部分,其中产生了ReactRoot和ReactWork两个类的实例。本文介绍下ReactRoot,ReactWork源码,只关注第一次调用render的过程。
文章中如有不当之处,欢迎交流指点。react版本16.8.2。在源码添加的注释在githubreact-source-learn。
回顾

在上篇分析后,最终得到如下函数调用过程。

在render方法中调用了legacyRenderSubtreeIntoContainer。

在legacyRenderSubtreeIntoContainer中调用legacyCreateRootFromDOMContainer获得了ReactRoot的实例root,然后调用unbatchedUpdates,其中的回调函数中调用了root.render方法。RootRoot是什么?他的render方法做了什么?

代码分析

通过对ReactRoot和ReactWork代码的简单分析,笔者做了如下类图,以帮助了解这两个类有哪些属性和方法。

有很多东西不是第一次调用render用到的,这里只关注第一次render所需要调用方法或使用的属性。

ReactRoot

这个类主要介绍其构造函数和render方法,构造函数是new时调用的,render方法是unbatchedUpdates的回调函数中使用的。代码如下:

// ReactRoot构造函数
// 构造函数主要是挂了一个_internalRoot在this上
function ReactRoot(
  container: DOMContainer, // dom节点
  isConcurrent: boolean, // 第一次render为false
  hydrate: boolean, // 第一次render为false
) {
  // 这个createContainer是packages/ReactFiberReconciler中的方法,
  // 返回的是一个OpaqueRoot的东西
  const root = createContainer(container, isConcurrent, hydrate);
  this._internalRoot = root;
}

// render实例方法 new 了ReactWork, 调用了then方法
// 调用了updateContainer方法
// 返回了ReactWork实例
ReactRoot.prototype.render = function(
  children: ReactNodeList,  // element
  callback: ?() => mixed, // ReactDOM.render(element, container, callback); callback
): Work {
  const root = this._internalRoot;
  const work = new ReactWork();
  callback = callback === undefined ? null : callback;
  if (callback !== null) {
    work.then(callback);
  }

  // updateContainer是packages/react-reconciler/ReactFiberReconciler.js中的
  // 一个方法,后边再说
  updateContainer(children, root, null, work._onCommit);
  return work;
};

先看构造函数
第一次render时new ReactRoot位于legacyCreateRootFromDOMContainer中,其调用代码如下:
return new ReactRoot(container, isConcurrent, shouldHydrate)
container是我们调用ReactDOM.render时的第二个参数,一个dom,但是里边的子节点已被处理了,isConcurrent这里是写死的false,shouldHydrate上文分析过,为false。
再看ReactRoot的构造函数,他调用了一个createContainer并将返回值挂到了_internalRoot属性。这个createContainer将在下一篇分析。

看下render方法

我们先看看第一次render是调用他的代码,root.render(children, callback);,其中children是ReactDOM.render的第一个参数,是个ReactElement, callback是第三个参数,通常不传。

这个render方法主要做了如下事:

new ReactWork -> work

调用work的then方法

调用updateContainer

返回work

这里值得注意的是ReactWork类,这个将在后文分析;还有updateContainer,这个将在后面的文章分析,这里搞清楚第一调用时的参数给的啥,即ReactElement,createContainer返回的root,null,ReactWork实例的_onCommit方法。

ReactWork

ReactWork的方法在第一次render时都有可能被调用到,如下代码为ReactWork类的定义:

// ReactWork的构造函数
function ReactWork() {
  this._callbacks = null;
  this._didCommit = false;
  // TODO: Avoid need to bind by replacing callbacks in the update queue with
  // list of Work objects.
  this._onCommit = this._onCommit.bind(this);
}

// then方法
ReactWork.prototype.then = function(onCommit: () => mixed): void {
  if (this._didCommit) { // 第一次render调用then时为false, 不走这里
    onCommit();
    return;
  }
  let callbacks = this._callbacks;
  if (callbacks === null) { // 第一次render是调用走这里
    callbacks = this._callbacks = [];
  }
  callbacks.push(onCommit);
};

// _onCommit方法
ReactWork.prototype._onCommit = function(): void {
  if (this._didCommit) { // 第一次render不走这里
    return;
  }
  this._didCommit = true;
  // 这个callbacks是调用.then方法是传进去的函数
  const callbacks = this._callbacks;
  if (callbacks === null) {
    return;
  }
  // TODO: Error handling.
  for (let i = 0; i < callbacks.length; i++) {
    const callback = callbacks[i];
    invariant(
      typeof callback === "function",
      "Invalid argument passed as callback. Expected a function. Instead " +
        "received: %s",
      callback,
    );
    callback();
  }
};

构造函数
构造函数不接受参数,做了一些初始化工作

then方法
then方法调用时是work.then(callback);,callback是ReactDOM的第三个参数
then方法的作用就是维护一个_callbacks队列,每次都将传进去的函数入队

_onCommit方法
这个方法的调用代码updateContainer(children, root, null, work._onCommit),其实是updateContainer的最后一个参数。
在这个里边将_didCommit置为true,回顾上边的ReactRoot的render方法,意味着这个方法被调用后在调ReactRoot.render是会直接执行callback的而不是入队。
然后是将_callbacks中的方法都执行了一遍。

小结

从上文的分析来看,接下来的重点是分析updateContainer这个方法,ReactWork的then方法是将callback入队,_onCommit是执行_callbacks中的所有方法,而调用_onCommit的是在updateContainer中,updateContainer实在ReactRoot.render方法中调用的,因此updateContainer应该是一个非常重要的东西。另外,ReactRoo.render方法是在unbatchedUpdates的回调函数中调用的,unbatchedUpdates也是一个参与后面调度的关键。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/103730.html

相关文章

  • React Fiber源码分析 第一篇

    摘要:系列文章源码分析第一篇源码分析第二篇同步模式源码分析第三篇异步状态源码分析第四篇归纳总结前言是在版本中的大更新,利用了闲余时间看了一些源码,做个小记录流程图源码分析先由编译,调用,入参为,打印出来可以看到,,分别代表着元素原生元素,回调函数 系列文章 React Fiber源码分析 第一篇 React Fiber源码分析 第二篇(同步模式) React Fiber源码分析 第三篇(...

    amc 评论0 收藏0
  • 剖析 React 源码:render 流程(二)

    摘要:就是,如果你不了解这个的话可以阅读下相关文档,是应用初始化时就会生成的一个变量,值也是,并且这个值不会在后期再被改变。这是我的剖析 React 源码的第三篇文章,如果你没有阅读过之前的文章,请务必先阅读一下 第一篇文章 中提到的一些注意事项,能帮助你更好地阅读源码。 文章相关资料 React 16.8.6 源码中文注释,这个链接是文章的核心,文中的具体代码及代码行数都是依托于这个仓库 热身...

    My_Oh_My 评论0 收藏0
  • ReactDOM.render源码解析-1

    摘要:本文将对源码做一个初步解析。首先在方法中校验参数是否合法,然后调用在中,调用拿到了的一个实例,调用拿到了,用于注入到,和作为返回值,调用开始调度过程在中,首先清理了中的所有子节点,然后了一个并返回是如何调度的是一个什么样的类的操作是在哪里 初步看了react-dom这个包的一些源码,发现其比react包要复杂得多,react包中基本不存在跨包调用的情况,他所做的也仅仅是定义了React...

    _Zhao 评论0 收藏0
  • react解析: render的FiberRoot(三)

    摘要:查看创建核心函数源码行调用函数创建是相关,不用管源码行这个指的是调用创建,下面我们将会说到对象源码行源码行函数中,首先创建了一个,然后又创建了一个,它们两者还是相互引用。 感谢 yck: 剖析 React 源码解析,本篇文章是在读完他的文章的基础上,将他的文章进行拆解和加工,加入我自己的一下理解和例子,便于大家理解。觉得yck写的真的很棒 。React 版本为 16.8.6,关于源码的...

    muddyway 评论0 收藏0
  • React源码解析之ReactDOM.render()

    摘要:一更新的方式有三种渲染接下来,我们就来看下源码二作用在提供的里渲染一个元素,并返回对该组件的引用常见的用法是这个官网网址源码服务端使用方法渲染节点是让服务端尽可能复用节点,提高性能元素容器应用渲染结束后,调用的函数错误抓取方法本质是返回 showImg(https://segmentfault.com/img/remote/1460000020064414?w=1240&h=641);...

    iKcamp 评论0 收藏0

发表评论

0条评论

macg0406

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<