React源码解析笔记---ReactDOM.render

前言:为了搞清楚react到底是个什么样的“框架”以及它的内部机制,因此开始阅读react源码。在阅读源码的时候,为了梳理流程,根据自己的理解以及网上的一些资料做了一些(大致流程的)笔记。笔记是当时的理解,写的时候,仍然有很多不理解的地方待后续完善。而写出来的部分,可能会有很多理解不到位甚至是错误的地方,一旦有新的理解或者发现了错误,会补充与修正。

react源码版本:16.8.6

ReactDOM.render用法:
import ReactDOM from 'react-dom';

ReactDOM.render(, document.getElementById('root));
ReactDOM.render源码(这里只摘录了主要代码):

代码路径:packages/react-dom/src/client/ReactDOM.js

const ReactDOM: Object = {
  ...
  render(
    element: React$Element<any>, // React Element,也就是上面的
    container: DOMContainer, // 一个dom节点,也就是上面的root
    callback: ?Function, // 回调函数,可不传
  ) {
    ...
    return legacyRenderSubtreeIntoContainer(
      null,
      element,
      container,
      false,
      callback,
    );
  },
  ...

可以看到这里调用了legacyRenderSubtreeIntoContainer函数并返回,然后找到该函数源码。

legacyRenderSubtreeIntoContainer函数代码:

代码路径:packages/react-dom/src/client/ReactDOM.js

// 将子组件树渲染进container中
function legacyRenderSubtreeIntoContainer(
  parentComponent: ?React$Component<any, any>, // 父组件,这里传入的是null
  children: ReactNodeList, // 子节点,这里是上面传入的
  container: DOMContainer, // 这里是root节点
  forceHydrate: boolean, // 是否进行组合,render传入的是false
  callback: ?Function, // 上面的回调函数
) {
 ...
  // TODO: Without `any` type, Flow says "Property cannot be accessed on any
  // member of intersection type." Whyyyyyy.
  // 这里是初次渲染,container上没有_reactRootContainer属性,root为false
  let root: Root = (container._reactRootContainer: any); 
  if (!root) {
    // 初次挂载
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
      container,
      forceHydrate,
    );
    if (typeof callback === 'function') {
      const originalCallback = callback;
      callback = function() {
        const instance = getPublicRootInstance(root._internalRoot);
        originalCallback.call(instance);
      };
    }
    // Initial mount should not be batched.
    unbatchedUpdates(() => {
      if (parentComponent != null) {
        root.legacy_renderSubtreeIntoContainer(
          parentComponent,
          children,
          callback,
        );
      } else {
        root.render(children, callback);
      }
    });
  } else {
    ...
  }
  return getPublicRootInstance(root._internalRoot);
}

可以看到,legacyRenderSubtreeIntoContainer函数做了几件事:

  1. 调用legacyCreateRootFromDOMContainer方法,获取ReactRoot;
  2. 重新定义callback函数;
  3. 调用unbatchedUpdates方法;
  4. 调用getPublicRootInstance方法并返回结果;
第一步:获取ReactRoot

代码路径:packages/react-dom/src/client/ReactDOM.js

// 清除container下的内容并获取返回ReactRoot赋值给上一层的root
function legacyCreateRootFromDOMContainer(
  container: DOMContainer, // root节点
  forceHydrate: boolean, // false
): Root {
  // 这里为false
  const shouldHydrate =
    forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
    
  // 进入判断,清除掉container下的所有内容
  if (!shouldHydrate) {
    let warned = false;
    let rootSibling;
    while ((rootSibling = container.lastChild)) {
      ...
      // 做了一些判断与提示的工作
	  ...
	  // 看函数名字可以知道,清除root下的内容
      container.removeChild(rootSibling);
    }
  }
   ...
   // 做了一些判断与提示的工作
   ...
  // 默认情况下root非异步
  const isConcurrent = false;
  return new ReactRoot(container, isConcurrent, shouldHydrate);
}

// ReactRoot构造函数
function ReactRoot(
  container: DOMContainer, // root节点
  isConcurrent: boolean, // false
  hydrate: boolean, // false
) {
  const root = createContainer(container, isConcurrent, hydrate);
  this._internalRoot = root;
}

// ReactRoot原型的一些方法(简化了下)
ReactRoot.prototype.render = function(
  children: ReactNodeList,
  callback: ?() => mixed,
): Work {
  const root = this._internalRoot;
  const work = new ReactWork();
  callback = callback === undefined ? null : callback;
  ...
  if (callback !== null) {
    work.then(callback);
  }
  updateContainer(children, root, null, work._onCommit);
  return work;
};
ReactRoot.prototype.unmount = function(callback: ?() => mixed): Work {
 ...
};
ReactRoot.prototype.legacy_renderSubtreeIntoContainer = function(
  parentComponent: ?React$Component<any, any>,
  children: ReactNodeList,
  callback: ?() => mixed,
): Work {
  ...
};
ReactRoot.prototype.createBatch = function(): Batch {
  ...
};

到这里可以得出一个结论,获取的ReactRoot是一个对象,包含以下几个属性:
_internalRoot
render
unmount
legacy_renderSubtreeIntoContainer
createBatch

但目前首先要关心的是_internalRoot,这个是下面的几个步骤要用到的,那就必须看一下createContainer方法;createContainer方法并不是在当前文件中定义,而是最终调用了ReactFiberRoot.js文件内的createFiberRoot方法;

代码路径:packages/react-reconciler/src/ReactFiberRoot.js

export function createFiberRoot(
  containerInfo: any, // container,这里是最初从render函数传进来的root dom节点
  isConcurrent: boolean, // false
  hydrate: boolean, //false
): FiberRoot {
  const root: FiberRoot = (new FiberRootNode(containerInfo, hydrate): any);
  
  // Cyclic construction. This cheats the type system right now because
  // stateNode is any.
  const uninitializedFiber = createHostRootFiber(isConcurrent);
  root.current = uninitializedFiber;
  uninitializedFiber.stateNode = root;
  
  return root;
}

// FiberRootNode构造函数
function FiberRootNode(containerInfo, hydrate) {
  this.current = null;
  this.containerInfo = containerInfo;
  this.pendingChildren = null;
  this.pingCache = null;
  this.pendingCommitExpirationTime = NoWork;
  this.finishedWork = null;
  this.timeoutHandle = noTimeout;
  this.context = null;
  this.pendingContext = null;
  this.hydrate = hydrate;
  this.firstBatch = null;
  this.callbackNode = null;
  this.callbackExpirationTime = NoWork;
  this.firstPendingTime = NoWork;
  this.lastPendingTime = NoWork;
  this.pingTime = NoWork;

  if (enableSchedulerTracing) {
    this.interactionThreadID = unstable_getThreadID();
    this.memoizedInteractions = new Set();
    this.pendingInteractionMap = new Map();
  }
}

由上面的代码可以看出来,_internalRoot其实就是一个包含众多属性的FiberRoot实例对象。它的current属性被赋值为createHostRootFiber的返回值。

createHostRootFiber其实是创建了一个Fiber对象,但这个Fiber的初始tag被设置为了HostRoot(代表这是一个宿主根,不能被其他节点嵌套),因此被称作RootFiber(与FiberRoot是两个概念)。

于是可以列出ReactRoot的结构了:

{
  _internalRoot: { // FiberRoot实例对象
    ...
    current: { //RootFiber实例对象
      ...
      stateNode //FiberRoot实例对象,也就是它的上上一层,
      ...
    }
    ...
  },
  render: function() {}
  unmount: function() {}
  legacy_renderSubtreeIntoContainer: function() {}
  createBatch: function() {}
}

也就是说:
FiberRoot.current = RootFiber;
RootFiber.stateNode = FiberRoot;

第二步:callback内传入instance

/////////////////////

第三步:调用unbatchedUpdates

React源码解析笔记—调度更新(一)

你可能感兴趣的:(React源码)