美文网首页
react和reactdom

react和reactdom

作者: TurnHug | 来源:发表于2023-10-18 10:33 被阅读0次

    react和reactdom有什么区别

    ReactDom 只做和浏览器或DOM相关的操作,例如:ReactDOM.render() 和 ReactDOM.findDOMNode()。如果是服务器端渲染,可以 ReactDOM.renderToString()。

    React 不仅能通过 ReactDOM 和Web页面打交道,还能用在服务器端SSR,移动端ReactNative和桌面端Electron。

    React 在v0.14之前是没有 ReactDOM 的,所有功能都包含在 React 里。从v0.14(2015-10)开始,React 才被拆分成React 和 ReactDOM。为什么要把 React 和 ReactDOM 分开呢?因为有了 ReactNative。React 只包含了 Web 和 Mobile 通用的核心部分,负责 Dom 操作的分到 ReactDOM 中,负责 Mobile 的包含在 ReactNative 中。

    ReactDom是React的一部分。ReactDOM是React和DOM之间的粘合剂,一般用来定义单一的组件,或者结合ReactDOM.findDOMNode()来使用。更重要的是ReactDOM包已经允许开发者删除React包添加的非必要的代码,并将其移动到一个更合适的存储库。

    react渲染组件方式

    ReactDOM.render()适用于React应用的根组件渲染,ReactDOM.createPortal()适用于将组件渲染到DOM树中的任意位置,而使用React-Portal库可以更方便地实现ReactDOM.createPortal()的功能。

    ReactDOM.render()做了什么

    ReactDOM.render函数是整个 React 应用程序首次渲染的入口函数,它的参数是什么,返回值是什么,函数内部做了什么?
    ReactDOM.render(<App />, document.getElementById("root"));
    首先看下首次渲染时候,函数调用栈。

    image.png
    render函数源码
    源码地址
    export function render(
      element: React$Element<any>, // 要渲染的组件
      container: Container, // 根容器
      callback: ?Function // 回调函数
    ) {
    
      return legacyRenderSubtreeIntoContainer(
        null,
        element,
        container,
        false,
        callback
      );
    }
    

    legacyRenderSubtreeIntoContainer函数
    源码地址
    内部实现:

    判断是否为第一次渲染,也就是首次挂载
    首次挂载,首先会创建FiberRoot,然后判断是否有回调函数,有就执行,最后进入updateContainer
    非首次,获取FiberRoot,然后判断是否有回调函数,有就执行,最后进入updateContainer
    首次和非首次,最大的区别在于是否创建FiberRoot和走不走批量更新。
    最后返回当前应用程序根组件的实例(getPublicRootInstance

    function legacyRenderSubtreeIntoContainer(
      parentComponent: ?React$Component<any, any>,
      children: ReactNodeList,
      container: Container,
      forceHydrate: boolean,
      callback: ?Function,
    ) {
      let root: RootType = (container._reactRootContainer: any);
      let fiberRoot;
    
      // 初始化也就是react, 第一次渲染
      if (!root) {
        // Initial mount
    
        /**
         * 首次挂载时,会通过 legacyCreateRootFromDOMContainer 方法创建 container._reactRootContainer 对象并赋值给 root
         */
        root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
          container,
          forceHydrate,
        );
    
        // 也就是ReactDOM.render时候, 产生了fiberRoot
        fiberRoot = root._internalRoot;
    
        /**
         * 判断是否存在callback, 也就是ReactDom.render中的第三个参数
         */
        if (typeof callback === 'function') {
          const originalCallback = callback;
          callback = function() {
            const instance = getPublicRootInstance(fiberRoot);
            // 通过实例去调用originalCallback方法
            originalCallback.call(instance);
          };
        }
        // 初始化挂载的时候, 不应该批量更新
        unbatchedUpdates(() => {
          updateContainer(children, fiberRoot, parentComponent, callback);
        });
      } else {
        fiberRoot = root._internalRoot;
        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);
    }
    

    legacyCreateRootFromDOMContainer函数
    legacyCreateRootFromDOMContainer ===> 。。。 ===> createFiberRoot。

    前面调用了一大堆函数,实际上最终目的就是创建Fiber,根据前序的截图来看,最终会去调用createFiberRoot。

    首先创建一个FiberRootNode
    然后创建RootFiber
    接着将两者关联起来
    接着初始化队列updateQueue
    最后返回FiberRootNode

      fiberRootNode.current = rootFiber;
      rootFiber.stateNode = fiberRootNode;
    

    源码地址
    createFiberRoot源码

    export function createFiberRoot(
      containerInfo: any,
      tag: RootTag,
      hydrate: boolean,
      hydrationCallbacks: null | SuspenseHydrationCallbacks,
    ): FiberRoot {
      // 创建fiberRoot(fiberRootNode)
      const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);
    
      if (enableSuspenseCallback) {
        root.hydrationCallbacks = hydrationCallbacks;
      }
      const uninitializedFiber = createHostRootFiber(tag);// 创建rootFiber(fiberNode)
    
      // 把rootFiber挂载到fiberRoot的current属性上  
      root.current = uninitializedFiber;
    
      // 把fiberRoot挂载到rootFiber的stateNode属性上
      uninitializedFiber.stateNode = root;
    
      // 初始化更新队列
      initializeUpdateQueue(uninitializedFiber);
    
      return root;
    }
    

    updateContainer函数

    源码地址

    这个函数主要是计算当前节点的lane优先级,创建update对象,加入更新队列,最后被调度。这三个知识点不属于本文所细讲,留个印象,后面再细讲。

    export function updateContainer(
      element: ReactNodeList,
      container: OpaqueRoot,
      parentComponent: ?React$Component<any, any>,
      callback: ?Function
    ): Lane {
      // 获取fiberRoort.current, 也就是rootFiber, Fiber树的头结点
      const current = container.current;
    
      /* 当前触发更新时候的时间戳 */
      const eventTime = requestEventTime();
    
      // console.log(eventTime, performance.now())
    
      // 计算当前节点lane(优先级)
      const lane = requestUpdateLane(current);
    
      if (enableSchedulingProfiler) {
        markRenderScheduled(lane);
      }
    
      const context = getContextForSubtree(parentComponent);
      if (container.context === null) {
        container.context = context;
      } else {
        container.pendingContext = context;
      }
    
      /**
       * 根据lane(优先级)计算当前节点的update对象
       */
      const update = createUpdate(eventTime, lane);
      // Caution: React DevTools currently depends on this property
      // being called "element".
      update.payload = { element };
    
      callback = callback === undefined ? null : callback;
      if (callback !== null) {
        update.callback = callback;
      }
    
      // 将update对象入队
      enqueueUpdate(current, update);
    
      // 调度当前的Fiber节点(也就是rootFiber)
      scheduleUpdateOnFiber(current, lane, eventTime);
    
      return lane;
    }
    

    解答开头提到的三个问题

    1.参数是什么:
    要渲染的子组件
    根容器
    要执行的回调函数

    2.返回值是什么:
    当前应用程序根组件的实例

    3.做了什么操作:
    创建FiberRoot,计算lane优先级,创建update对象,加入更新队列,最后被调度器调度更新。

    相关文章

      网友评论

          本文标题:react和reactdom

          本文链接:https://www.haomeiwen.com/subject/xktcidtx.html