一、ReactDOM.render 都干啥了

我们在写react的时候,最后一步肯定是

ReactDOM.render(
    <div>
        <Home name="home"/>
    </div>
    ,
    document.getElementById('app')
);

 

我们上面得知jsx被解析成了虚拟dom对象,我们把一个对象和一个dom传入render方法就得到了我们的页面,好神奇呀,我们开始撸到render方法:

const ReactDOM: Object = {

  render(
    element: React$Element<any>,  // react组件对象
    container: DOMContainer,   // 就是id为app的那个dom
    callback: ?Function, // callback 没有
  ) {
    return legacyRenderSubtreeIntoContainer(
      null,
      element,
      container,
      false,
      callback,
    );
  }

}

抛开typeScript那些恶心的类型限定不谈,我们发现render的实质就是调用并返回   legacyRenderSubtreeIntoContainer   这个函数执行后的结果,你看这个函数的命名:

legacy : 遗产  +  render: 渲染  +  subtree: 子树  +  into: 到 +  container: 容器

爱几把咋翻译咋翻译,大致意思就是说把 虚拟的dom树渲染到真实的dom容器中。此函数应当荣当 核心函数 宝座

 

二、legacyRenderSubtreeIntoContainer 又干了啥?

还是撸到丫的源码:

 

function legacyRenderSubtreeIntoContainer(
  parentComponent: ?React$Component<any, any>,  // null
  children: ReactNodeList, // element 虚拟dom树  
  container: DOMContainer, // html中的dom根对象
  forceHydrate: boolean, // false 服务器端渲染标识
  callback: ?Function, // 回调函数  没有
) {
  // 对container进行校验
  invariant(
    isValidContainer(container),
    'Target container is not a DOM element.',
  );
  // 取root对象,一般如果非服务器端渲染这个root是不存在的
  let root: Root = (container._reactRootContainer: any);
  // 进入浏览器端渲染流程
  if (!root) {
    //  人工制造root,附加了一堆fiber的东西到_reactRootContainer
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
      container,
      forceHydrate,
    );

    if (typeof callback === 'function') {
      const originalCallback = callback;
      callback = function() {
        // 该变callback的this为 instance
        const instance = DOMRenderer.getPublicRootInstance(root._internalRoot);
        originalCallback.call(instance);
      };
    }
    DOMRenderer.unbatchedUpdates(() => {
      if (parentComponent != null) {
        // 向真实dom中挂载虚拟dom
        root.legacy_renderSubtreeIntoContainer(
          parentComponent,
          children,
          callback,
        );
      } else {
        // 多么直白的语义
        root.render(children, callback);
      }
    });
  } else {
    // 还是先整一下callback
    if (typeof callback === 'function') {
      const originalCallback = callback;
      callback = function() {
        const instance = DOMRenderer.getPublicRootInstance(root._internalRoot);
        originalCallback.call(instance);
      };
    }
    // 还是上面那一套
    if (parentComponent != null) {
      // 向root中挂载dom
      root.legacy_renderSubtreeIntoContainer(
        parentComponent,
        children,
        callback,
      );
    } else {
      root.render(children, callback);
    }
  }
  // 返回container 中的dom
  return DOMRenderer.getPublicRootInstance(root._internalRoot);
}

通过看这个核心函数的代码,发现它其中有3个谜团:

1. _reactRootContainer 的制造:legacyCreateRootFromDOMContainer

   这个函数会制造一个对象挂载到真实的dom根节点上,有了这个对象,执行该对象上的一些方法可以将虚拟dom变成dom树挂载到根节点上

2. DOMRenderer.unbatchedUpdates

   它的回调执行了挂载dom结构的方法

3.  root.legacy_renderSubtreeIntoContainer 和 root.render

   如果有parentComponent 这个东西,就执行root.render 否则 root.legacy_renderSubtreeIntoContainer

 

三、跟进谜团

1.root的制造

找到 legacyCreateRootFromDOMContainer 函数:

 1 function legacyCreateRootFromDOMContainer(
 2   container: DOMContainer,
 3   forceHydrate: boolean, // false
 4 ): Root {
 5   const shouldHydrate =
 6     forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
 7   // 是否需要服务器端渲染
 8   if (!shouldHydrate) {
 9     let warned = false;
10     let rootSibling;
11     while ((rootSibling = container.lastChild)) {
12       if (__DEV__) {
13         ...
14       }
15       // 将dom根节点清空
16       container.removeChild(rootSibling);
17     }
18   }
19   if (__DEV__) {
20     ...
21   }
22   const isAsync = false;
23   return new ReactRoot(container, isAsync, shouldHydrate);
24 }
View Code

相关文章:

  • 2023-02-06
  • 2021-09-06
  • 2023-02-06
  • 2023-03-16
  • 2021-12-21
  • 2021-09-01
猜你喜欢
  • 2021-09-28
  • 2022-12-23
  • 2022-12-23
  • 2021-04-17
  • 2022-12-23
  • 2022-12-23
相关资源
相似解决方案