Skip to content
目录

ReactDOM.render 做了什么?

ReactDOM.render

以下代码是render方法的实现, render方法其实接受三个参数, callback平时用的较少

typescript
render(
    element: React$Element<any>,
    container: DOMContainer,
    callback: ?Function,
  ) {
    return legacyRenderSubtreeIntoContainer(
      null,
      element,
      container,
      false,
      callback,
    );
  }

创建ReactRoot

首先,render方法执行的时候,调用legacyRenderSubtreeIntoContainer 方法创建ReactRoot对象

typescript
function legacyRenderSubtreeIntoContainer(
  // 父节点
  parentComponent: ?React$Component<any, any>, 
  // ReactDOM.render的第一个参数对应的ReactElement
  children: ReactNodeList,
  // ReactDOM.render的第二个参数 
  container: DOMContainer,
  // 服务端渲染相关
  forceHydrate: boolean,
  callback: ?Function,
) {
  // 在dom节点增加属性_reactRootContainer
  let root: Root = (container._reactRootContainer: any);
  if (!root) {
    // 创建ReactRoot
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
      container,
      forceHydrate,
    );
    if (typeof callback === 'function') {
      const originalCallback = callback;
      callback = function() {
        const instance = getPublicRootInstance(root._internalRoot);
        originalCallback.call(instance);
      };
    }
    // render方法,放在unbatchedUpdates里面更新
    // 意思是当前的更新不需要批量更新,因为是首次渲染,所以会尽快渲染出页面
    unbatchedUpdates(() => {
      // 执行ReactRoot的render方法
      root.render(children, callback);
    });
  } else {
    /** do some things **/
  }
  return getPublicRootInstance(root._internalRoot);
}

TIP

执行legacyCreateRootFromDOMContainer方法实例化ReactRoot,在实例化之前,针对不需要服务端 渲染的情况,假如root节点下存在DOM, 会将原本的DOM节点删除

legacyCreateRootFromDOMContainer方法

typescript
function legacyCreateRootFromDOMContainer(
  container: DOMContainer,
  forceHydrate: boolean,
): Root {
  // 在目前的版本中 concurrent mode 默认是关闭的
  const isConcurrent = false;
  return new ReactRoot(container, isConcurrent, shouldHydrate);
}

DANGER

注意: isConcurrent表示的是是否开启并发模式, 在当前版本中 默认是关闭的

ReactRoot方法的实现

  1. new ReactRoot的时候,创建了FiberRoot节点
  2. 在上面的代码中,执行了root.render, 调用了updateContainer方法做一个具体的更新
typescript
function ReactRoot(
  container: DOMContainer,
  isConcurrent: boolean,
  hydrate: boolean,
) {
  // 创建FiberRoot节点
  const root = createContainer(container, isConcurrent, hydrate);
  this._internalRoot = root;
}
typescript
ReactRoot.prototype.render = function(
  children: ReactNodeList,
  callback: ?() => mixed,
): Work {
  const root = this._internalRoot;
  updateContainer(children, root, null, work._onCommit);
};

updateContainer的执行过程

创建FiberRoot节点

FiberRoot节点由createContainer方法创建,接着调用createFiberRoot方法

typescript
export function createFiberRoot(
  containerInfo: any,
  isConcurrent: boolean,
  hydrate: boolean,
): FiberRoot {
  // 创建RootFiber
  const uninitializedFiber = createHostRootFiber(isConcurrent);
  
  // 当前root是FiberRoot
  let root = ({
    // 指向RootFiber
    current: uninitializedFiber,
    // 指向root的dom节点
    containerInfo: containerInfo,
    pendingChildren: null,
    pingCache: null,
    earliestPendingTime: NoWork,
    latestPendingTime: NoWork,
    earliestSuspendedTime: NoWork,
    latestSuspendedTime: NoWork,
    latestPingedTime: NoWork,
    didError: false,
    pendingCommitExpirationTime: NoWork,
    finishedWork: null,
    timeoutHandle: noTimeout,
    context: null,
    pendingContext: null,
    hydrate,
    nextExpirationTimeToWorkOn: NoWork,
    expirationTime: NoWork,
    firstBatch: null,
    nextScheduledRoot: null,
  }: BaseFiberRootProperties);
  
  // 将RootFiber的stateNode指向 FiberRoot
  uninitializedFiber.stateNode = root;
  
  return root
}

创建RootFiber

typescript
export function createHostRootFiber(isConcurrent: boolean): Fiber {
  // 当前版本中,还没有开启ConcurrentMode
  let mode = isConcurrent ? ConcurrentMode | StrictMode : NoContext;
  if (enableProfilerTimer && isDevToolsPresent) {
    mode |= ProfileMode;
  }
  return createFiber(HostRoot, null, null, mode);
}

DANGER

注意看, 当前的RootFiber类型是 HostRoot

至此,创建完了一下基本的节点,然后执行root.render, 调用了updateContainer 方法做一个具体的更新

具体结构如下图

react-root.png

备案号: 浙ICP备2023000081号