美文网首页
React源码-ReactDOM.render

React源码-ReactDOM.render

作者: suphu | 来源:发表于2020-12-16 21:44 被阅读0次

    版本16.13.0

    ReactDOM.render(<App />, document.getElementById('root'));

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

    ++实际调用legacyRenderSubtreeIntoContainer并设置它的第四个参数shouldHydrate = false 客户端渲染都是false,true服务端渲染用于是否复用子节点++

    function legacyRenderSubtreeIntoContainer(
      parentComponent: ?React$Component<any, any>,
      children: ReactNodeList,
      container: DOMContainer,
      forceHydrate: boolean,
      callback: ?Function,
    ) {
     // TODO: Without `any` type, Flow says "Property cannot be accessed on any
      // member of intersection type." Whyyyyyy.
      let root: _ReactSyncRoot = (container._reactRootContainer: any);
      let fiberRoot;
      if (!root) {
        // Initial mount  根据container初始化创建ReactSyncRoot
        root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
          container,
          forceHydrate,
        );
        // ReactSyncRoot._internalRoot获取fiberRoot
        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. 初始化不批量更新 直接执行fn
        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);
    
    1. 先判断container(document.getElementById('root')是否有_reactRootContainer属性,没有则赋值创建ReactSyncRoot,legacyCreateRootFromDOMContainer
    function legacyCreateRootFromDOMContainer(
      container: DOMContainer,
      forceHydrate: boolean,
    ): _ReactSyncRoot {
      const shouldHydrate =
        forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
      // First clear any existing content. 
      if (!shouldHydrate) {
        let warned = false;
        let rootSibling;
        // !shouldHydrate下遍历containerdom下元素,删除子元素
        while ((rootSibling = container.lastChild)) {
          container.removeChild(rootSibling);
        }
      }
      // Legacy roots are not batched.
      return new ReactSyncRoot(
        container,
        LegacyRoot,
        shouldHydrate
          ? {
              hydrate: true,
            }
          : undefined,
      );
    }
    
    

    ++由上可以看到在非shouldHydrate下会对container dom下的子元素全部删除,所以大家在html模板下div添加子元素是无效的,最后都会被删除++

    function ReactSyncRoot(
      container: DOMContainer, // 非hydrate下删除子元素的dom 
      tag: RootTag, // LegacyRoot=0类型 
      options: void | RootOptions,
    ) {
      // _internalRoot = FiberRoot
      this._internalRoot = createRootImpl(container, tag, options);
    }
    
    function createRootImpl(
      container: DOMContainer,
      tag: RootTag, // LegacyRoot
      options: void | RootOptions,
    ) {
      // Tag is either LegacyRoot or Concurrent Root
      const hydrate = options != null && options.hydrate === true;
      const hydrationCallbacks =
        (options != null && options.hydrationOptions) || null;
      const root = createContainer(container, tag, hydrate, hydrationCallbacks);
      markContainerAsRoot(root.current, container);
      if (hydrate && tag !== LegacyRoot) {
        const doc =
          container.nodeType === DOCUMENT_NODE
            ? container
            : container.ownerDocument;
        eagerlyTrapReplayableEvents(doc);
      }
      return root;
    }
    
    export function createContainer(
      containerInfo: Container,
      tag: RootTag,
      hydrate: boolean,
      hydrationCallbacks: null | SuspenseHydrationCallbacks,
    ): OpaqueRoot {
      return createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks);
    }
    
    export function createFiberRoot(
      containerInfo: any, // document.getElementById('root') dom 
      tag: RootTag, // LegacyRoot=0
      hydrate: boolean,
      hydrationCallbacks: null | SuspenseHydrationCallbacks,
    ): FiberRoot {
      // 创建FiberRoot对象
      const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);
      if (enableSuspenseCallback) {
        root.hydrationCallbacks = hydrationCallbacks;
      }
      // Cyclic construction. This cheats the type system right now because
      // stateNode is any.
      // 创建rootFiber
      const uninitializedFiber = createHostRootFiber(tag);
      root.current = uninitializedFiber;
      uninitializedFiber.stateNode = root;
    
      return root;
    }
    
    

    ++ReactSyncRoot-createRootImpl-createContainer-createFiberRoot调用顺序,最后new FiberRootNode 创建了 tag=LegacyRoot的==FiberRoot==++

    export function createHostRootFiber(tag: RootTag): Fiber {
      let mode;
      if (tag === ConcurrentRoot) {
        mode = ConcurrentMode | BatchedMode | StrictMode;
      } else if (tag === BatchedRoot) {
        mode = BatchedMode | StrictMode;
      } else {
        mode = NoMode;
      }
      if (enableProfilerTimer && isDevToolsPresent) {
        // Always collect profile timings when DevTools are present.
        // This enables DevTools to start capturing timing at any point–
        // Without some nodes in the tree having empty base times.
        mode |= ProfileMode;
      }
      return createFiber(HostRoot, null, null, mode);
    }
    
    const createFiber = function(
      tag: WorkTag, // HostRoot=3
      pendingProps: mixed,
      key: null | string,
      mode: TypeOfMode, // NoMode
    ): Fiber {
      // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
      return new FiberNode(tag, pendingProps, key, mode);
    };
    
    

    ++后继续创建了tag=HostRoot,mode=NoMode的==RootFiber==对象(createHostRootFiber)++

    2.从ReactSyncRoot中取出_internalRoot则创建的FiberRoot对象,第一次调用unbatchedUpdates函数来不批量更新,直接执行内部的fn
     unbatchedUpdates(() => {
          updateContainer(children, fiberRoot, parentComponent, callback);
     });
     
     export function updateContainer(
      element: ReactNodeList, // app
      container: OpaqueRoot, // fiberRoot
      parentComponent: ?React$Component<any, any>,
      callback: ?Function,
    ): ExpirationTime {
      // fiber的root
      const current = container.current;
      // 计算新开始的时间
      const currentTime = requestCurrentTime();
      const suspenseConfig = requestCurrentSuspenseConfig();
      // 计算过期时间,用于React优先级 异步
      const expirationTime = computeExpirationForFiber(
        currentTime,
        current,
        suspenseConfig,
      );
      return updateContainerAtExpirationTime(
        element,
        container,
        parentComponent,
        expirationTime,
        suspenseConfig,
        callback,
      );
    }
       
       
    export function updateContainerAtExpirationTime(
      element: ReactNodeList,
      container: OpaqueRoot,
      parentComponent: ?React$Component<any, any>,
      expirationTime: ExpirationTime,
      suspenseConfig: null | SuspenseConfig,
      callback: ?Function,
    ) {
      return scheduleRootUpdate(
        current,
        element,
        expirationTime,
        suspenseConfig,
        callback,
      );
    } 
    
    
    function scheduleRootUpdate(
      current: Fiber,
      element: ReactNodeList,
      expirationTime: ExpirationTime,
      suspenseConfig: null | SuspenseConfig,
      callback: ?Function,
    ) {
      // 根据expirationTime 创建更新的update
      const update = createUpdate(expirationTime, suspenseConfig);
      // Caution: React DevTools currently depends on this property
      // being called "element".
      // 设置更新payload app组件
      update.payload = {element};
      callback = callback === undefined ? null : callback;
      if (callback !== null) {
        warningWithoutStack(
          typeof callback === 'function',
          'render(...): Expected the last optional `callback` argument to be a ' +
            'function. Instead received: %s.',
          callback,
        );
        update.callback = callback;
      }
      // 把此次更新入队
      enqueueUpdate(current, update);
      // 进行任务调度
      scheduleWork(current, expirationTime);
    
      return expirationTime;
    }
        
    

    ++-根据currentTime时间来计算expirationTime时间++

    ++-创建update对象,可以根据next来指向下个update++

    export function createUpdate(
      expirationTime: ExpirationTime,
      suspenseConfig: null | SuspenseConfig,
    ): Update<*> {
      let update: Update<*> = {
        expirationTime,
        suspenseConfig,
        tag: UpdateState,
        payload: null,
        callback: null,
        next: null,
        nextEffect: null,
      };
      return update;
    }
    

    ++-把update对象放入到updateQueue中++

    ++-scheduleWork进入任务调度中++

    Todo:expirationTime会在后续说明

    Todo:update,updateQueue会在后续说明

    总结:

    • 创建了 ReactRoot对象
    • 创建了FiberRoot,FiberRoot=ReactRoot._internalRoot
    • 创建了RootFiber,FiberRoot.current=RootFiber
    • 创建 update
    • update入队,scheduleWork进入调度阶段

    注:在render的第二个参数dom,在客户端渲染会删除掉全部的子元素

    相关文章

      网友评论

          本文标题:React源码-ReactDOM.render

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