美文网首页
ReactDOM的render和Fiber源码解析

ReactDOM的render和Fiber源码解析

作者: Ahungrynoob | 来源:发表于2019-08-06 09:48 被阅读0次

    render()

    ReactDOM.render(element, container[, callback])
    

    在提供的 container 里渲染一个 React 元素,并返回对该组件的引用(或者针对无状态组件返回 null)。

    如果 React 元素之前已经在 container 里渲染过,这将会对其执行更新操作,并仅会在必要时改变 DOM 以映射最新的 React 元素。

    如果提供了可选的回调函数,该回调将在组件被渲染或更新之后被执行。

    注意:

    ReactDOM.render() 会控制你传入容器节点里的内容。当首次调用时,容器节点里的所有 DOM 元素都会被替换,后续的调用则会使用 React 的 DOM 差分算法(DOM diffing algorithm)进行高效的更新。

    ReactDOM.render() 不会修改容器节点(只会修改容器的子节点)。可以在不覆盖现有子节点的情况下,将组件插入已有的 DOM 节点中。

    ReactDOM.render() 目前会返回对根组件 ReactComponent 实例的引用。 但是,目前应该避免使用返回的引用,因为它是历史遗留下来的内容,而且在未来版本的 React 中,组件渲染在某些情况下可能会是异步的。 如果你真的需要获得对根组件 ReactComponent 实例的引用,那么推荐为根元素添加 callback ref

    使用 ReactDOM.render() 对服务端渲染容器进行 hydrate 操作的方式已经被废弃,并且会在 React 17 被移除。作为替代,请使用 hydrate()

    packages/react-dom/ReactDOM.js 源码解析

    时序图.png
    render(
        element,
        container,
        callback,
      ) {
        invariant(
          isValidContainer(container),
          'Target container is not a DOM element.',
        );
        if (__DEV__) {
          warningWithoutStack(
            !container._reactHasBeenPassedToCreateRootDEV,
            'You are calling ReactDOM.render() on a container that was previously ' +
              'passed to ReactDOM.%s(). This is not supported. ' +
              'Did you mean to call root.render(element)?',
            enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot',
          );
        }
        return legacyRenderSubtreeIntoContainer(
          null,
          element,
          container,
          false,
          callback,
        );
      }
    

    ReactDOM.render方法中调用了legacyRenderSubtreeIntoContainer方法:

    function legacyRenderSubtreeIntoContainer(
      parentComponent,
      children,
      container,
      forceHydrate,
      callback,
    ) {
      if (__DEV__) {
        topLevelUpdateWarnings(container);
        warnOnInvalidCallback(callback === undefined ? null : callback, 'render');
      }
    
      // TODO: Without `any` type, Flow says "Property cannot be accessed on any
      // member of intersection type." Whyyyyyy.
      let root = container._reactRootContainer;
      let fiberRoot;
      if (!root) {
        // Initial mount
        root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
          container,
          forceHydrate,
        );
        fiberRoot = root._internalRoot;
        if (typeof callback === 'function') {
          const originalCallback = callback;
          callback = function() {
            const instance = getPublicRootInstance(fiberRoot);
            originalCallback.call(instance);
          };
        }
        // Initial mount should not be batched.
        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);
    }
    
    • 如果没有container._reactRootContainer值,则说明是第一次挂载到该container下,这时候,react在执行legacyCreateRootFromDOMContainer的会清空该dom下的子元素,并返回一个ReactSyncRoot类型的对象。并且第一次挂载的时候,shouldHydrate是为false的。
    • 经过legacyCreateRootFromDOMContainer 之后,container的数据是这样的:
    container._reactRootContainer = ReactRoot (from legacyCreateRootFromDOMContainer);
    container._reactRootContainer._internalRoot = FiberRoot
    container._reactRootContainer._internalRoot.current = FiberNode (HostRoot)
    container._reactRootContainer._internalRoot.current.stateNode = container._reactRootContainer._internalRoot = FiberRoot
    

    FiberRoot和Fiber的数据结构:

    function FiberRootNode(containerInfo, tag, hydrate) {
      this.tag = tag;
      this.current = null;
      this.containerInfo = containerInfo;
      this.pendingChildren = null;
      this.pingCache = null;
      this.finishedExpirationTime = 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();
      }
    }
    
    function FiberNode(
      tag,
      pendingProps,
      key,
      mode,
    ) {
      // Instance
      this.tag = tag;
      this.key = key;
      this.elementType = null;
      this.type = null;
      this.stateNode = null;
    
      // Fiber
      this.return = null;
      this.child = null;
      this.sibling = null;
      this.index = 0;
    
      this.ref = null;
    
      this.pendingProps = pendingProps;
      this.memoizedProps = null;
      this.updateQueue = null;
      this.memoizedState = null;
      this.dependencies = null;
    
      this.mode = mode;
    
      // Effects
      this.effectTag = NoEffect;
      this.nextEffect = null;
    
      this.firstEffect = null;
      this.lastEffect = null;
    
      this.expirationTime = NoWork;
      this.childExpirationTime = NoWork;
    
      this.alternate = null;
    }
    

    接下去就是关键的updateContainer函数了。这个函数牵涉的面比较多,我们下一片再说。

    相关文章

      网友评论

          本文标题:ReactDOM的render和Fiber源码解析

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