zoukankan      html  css  js  c++  java
  • React源码解携(二): 走一趟render流程

    总结

    • 我看到的四者关系

    render

    // 渲染函数
    function render(element, container, callback) {
      // element: React.Component元素这个是react的工作, 等会看
      // container: 这个传入的dom元素
      // callback: 没传
      if (!isValidContainer(container)) { // 判断是否为合理的dom元素
        {
          throw Error( "Target container is not a DOM element." );
        }
      }
    
      {
        // 是否被标记问react的根元素
        var isModernRoot = isContainerMarkedAsRoot(container) && container._reactRootContainer === undefined;
    
        if (isModernRoot) {
          error('You are calling ReactDOM.render() on a container that was previously ' + 'passed to ReactDOM.createRoot(). This is not supported. ' + 'Did you mean to call root.render(element)?');
        }
      }
    
      // 将dom节点渲染到container中
      return legacyRenderSubtreeIntoContainer(null, element, container, false, callback);
    }
    

    isValidContainer

    function isValidContainer(node) {
      // node: dom元素
      return !!(node && 
        (node.nodeType === ELEMENT_NODE || // 元素
        node.nodeType === DOCUMENT_NODE || // 整个文档
        node.nodeType === DOCUMENT_FRAGMENT_NODE || // 轻量级的Document对象
        node.nodeType === COMMENT_NODE && // 注释元素 就必须判断 node-value了 TODO: 这里不理解注释元素,是如何处理的
        node.nodeValue === ' react-mount-point-unstable ')
      );
    }
    

    isContainerMarkedAsRoot

    var randomKey = Math.random().toString(36).slice(2);
    var internalInstanceKey = '__reactFiber$' + randomKey;
    var internalPropsKey = '__reactProps$' + randomKey;
    var internalContainerInstanceKey = '__reactContainer$' + randomKey;
    var internalEventHandlersKey = '__reactEvents$' + randomKey;
    function precacheFiberNode(hostInst, node) {
      node[internalInstanceKey] = hostInst;
    }
    function markContainerAsRoot(hostRoot, node) {
      // hostRoot: RootFiber
      // node: containerDom
      // containerDom.__reactContainer = RootFiber
      node[internalContainerInstanceKey] = hostRoot;
    }
    function unmarkContainerAsRoot(node) {
      node[internalContainerInstanceKey] = null;
    }
    // 判断节点是否有internalContainerInstanceKey: TODO: 是否可以理解为判断根组件?
    function isContainerMarkedAsRoot(node) {
      return !!node[internalContainerInstanceKey];
    } 
    

    legacyRenderSubtreeIntoContainer

    // 过时的, 将子树渲染到Container中
    function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {
      // parentComponent: 父组件 null
      // children: 子组件 element
      // container: 挂载元素 container
      // forceHydrate: 是否为服务端渲染ssr
      // callback: 回调函数
      {
        topLevelUpdateWarnings(container); // 关于container 输出错误
        warnOnInvalidCallback$1(callback === undefined ? null : callback, 'render'); // 判断callback是否为一个函数
      }
    
      // fb开发者逗笑我了, 看源码呗, 这句话, 为啥打印了.
      // TODO: Without `any` type, Flow says "Property cannot be accessed on any
      // member of intersection type." Whyyyyyy.
    
      var root = container._reactRootContainer; // 获取根节点
      var fiberRoot; // fiber的根节点
    
      if (!root) { // 没获取到, 证明这个就是根节点
        // Initial mount
        root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);
        // FiberRootNode
        fiberRoot = root._internalRoot;  // createContainer(container, tag, hydrate);
    
        if (typeof callback === 'function') { // 处理回调函数
          var originalCallback = callback;
    
          callback = function () {
            var instance = getPublicRootInstance(fiberRoot);
            originalCallback.call(instance);
          };
        } // Initial mount should not be batched.
    
    
        unbatchedUpdates(function () { // TODO: 执行更新流程, 明天再看
          updateContainer(children, fiberRoot, parentComponent, callback); // 这一步就把我们的children 渲染上去了
        });
      } else {
        fiberRoot = root._internalRoot;
    
        if (typeof callback === 'function') {
          var _originalCallback = callback;
    
          callback = function () {
            var instance = getPublicRootInstance(fiberRoot);
    
            _originalCallback.call(instance);
          };
        } // Update
    
    
        updateContainer(children, fiberRoot, parentComponent, callback);
      }
    
      return getPublicRootInstance(fiberRoot);
    }
    

    topLevelUpdateWarnings

    {
      topLevelUpdateWarnings = function (container) {
        if (container._reactRootContainer && container.nodeType !== COMMENT_NODE) {
          // 首次执行的时候, container还不是根节点, 不会走到这里
          // 判断是否为根节点, 如果是的话, 类型不是注释节点
          var hostInstance = findHostInstanceWithNoPortals(container._reactRootContainer._internalRoot.current);
          if (hostInstance) {
            if (hostInstance.parentNode !== container) {
              error('render(...): It looks like the React-rendered content of this ' + 'container was removed without using React. This is not ' + 'supported and will cause errors. Instead, call ' + 'ReactDOM.unmountComponentAtNode to empty a container.');
            }
          }
        }
    
        var isRootRenderedBySomeReact = !!container._reactRootContainer;
        var rootEl = getReactRootElementInContainer(container); // 获取根元素, 第一遍渲染, 这里返回了null
        var hasNonRootReactChild = !!(rootEl && getInstanceFromNode(rootEl)); // false
    
        if (hasNonRootReactChild && !isRootRenderedBySomeReact) { // 首次不会走这里
          error('render(...): Replacing React-rendered children with a new root ' + 'component. If you intended to update the children of this node, ' + 'you should instead have the existing children update their state ' + 'and render the new components instead of calling ReactDOM.render.');
        }
    
        if (container.nodeType === ELEMENT_NODE && container.tagName && container.tagName.toUpperCase() === 'BODY') {
          // 如果是文档挂在了Body上, 报错. 因为有很多的第三方脚本和浏览器插件都放在body下面, react一更新就没了
          error('render(): Rendering components directly into document.body is ' + 'discouraged, since its children are often manipulated by third-party ' + 'scripts and browser extensions. This may lead to subtle ' + 'reconciliation issues. Try rendering into a container element created ' + 'for your app.');
        }
      };
    }
    

    getReactRootElementInContainer

    function getReactRootElementInContainer(container) {
      if (!container) {
        return null;
      }
    
      if (container.nodeType === DOCUMENT_NODE) { // 如果是document, 就返回document元素
        return container.documentElement;
      } else {
        return container.firstChild; // 否则返回第一个元素, 但是这里是null, 因为后面没有元素了
      }
    }
    

    warnOnInvalidCallback

    function warnOnInvalidCallback$1(callback, callerName) { // 判断callback是否为一个函数
      {
        if (callback !== null && typeof callback !== 'function') {
          error('%s(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callerName, callback);
        }
      }
    }
    

    legacyCreateRootFromDOMContainer

    // 从container中创建根节点
    function legacyCreateRootFromDOMContainer(container, forceHydrate) {
      // container: 挂载节点
      // forceHydrate: 是否进行强制ssr
      // 判断是否进行ssr
      var shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container); // First clear any existing content.
    
      if (!shouldHydrate) { // 不是ssr, 这里开始清空元素
        var warned = false;
        var rootSibling;
    
        while (rootSibling = container.lastChild) { // 判断根节点下面是否含有子元素
          {
            if (!warned && rootSibling.nodeType === ELEMENT_NODE && rootSibling.hasAttribute(ROOT_ATTRIBUTE_NAME)) { // 如果含有子元素, 并且子元素是react元素, 报错
              warned = true;
    
              error('render(): Target node has markup rendered by React, but there ' + 'are unrelated nodes as well. This is most commonly caused by ' + 'white-space inserted around server-rendered markup.');
            }
          }
    
          container.removeChild(rootSibling);
        }
      }
    
      {
        if (shouldHydrate && !forceHydrate && !warnedAboutHydrateAPI) { // 处理ssr?
          warnedAboutHydrateAPI = true;
          // React 18将会停止render 进行ssr更新, 并提供hydrate进行.
          warn('render(): Calling ReactDOM.render() to hydrate server-rendered markup ' + 'will stop working in React v18. Replace the ReactDOM.render() call ' + 'with ReactDOM.hydrate() if you want React to attach to the server HTML.');
        }
      }
    
      return createLegacyRoot(container, shouldHydrate ? {
        hydrate: true
      } : undefined);
    }
    

    shouldHydrateDueToLegacyHeuristic

    function shouldHydrateDueToLegacyHeuristic(container) { // TODO: 返回根节点是否应该更新?
      var rootElement = getReactRootElementInContainer(container); // 获取根节点
      return !!(rootElement && rootElement.nodeType === ELEMENT_NODE && rootElement.hasAttribute(ROOT_ATTRIBUTE_NAME));
    }
    

    createLegacyRoot

    // 创建根节点
    function createLegacyRoot(container, options) {
      debugger
      return new ReactDOMBlockingRoot(container, LegacyRoot, options);
    }
    
    // 创建老的一种根节点
    function ReactDOMBlockingRoot(container, tag, options) {
      this._internalRoot = createRootImpl(container, tag, options);
    }
    

    createRootImpl

    // 创建根节点接口
    function createRootImpl(container, tag, options) {
      // Tag is either LegacyRoot or Concurrent Root // tag表示, 你创建了一个老版本的root, 还是一个同时更新的root
      // 因为, 我们没有传入任何options
      var hydrate = options != null && options.hydrate === true; // false, 不是ssr
      var hydrationCallbacks = options != null && options.hydrationOptions || null; // null
      var mutableSources = options != null && options.hydrationOptions != null && options.hydrationOptions.mutableSources || null; // null
      var root = createContainer(container, tag, hydrate);
      // root就是FiberRootNode
      // FiberRootNode.containerInfo 指向了 container的dom元素
      // FiberRootNode.current 指向了 RootFiber
      // RootFiber.stateNode 指向了 FiberRootNode
      markContainerAsRoot(root.current, container);
      //  container的dom元素.__reactContainer$ = RootFiber
      var containerNodeType = container.nodeType;
    
      {
        var rootContainerElement = container.nodeType === COMMENT_NODE ? container.parentNode : container;
        listenToAllSupportedEvents(rootContainerElement); // 这里不再继续看事件的筛选添加过程.
      }
    
      if (mutableSources) { // 这是是false
        for (var i = 0; i < mutableSources.length; i++) {
          var mutableSource = mutableSources[i];
          registerMutableSourceForHydration(root, mutableSource);
        }
      }
    
      return root;
    }
    
    function createContainer(containerInfo, tag, hydrate, hydrationCallbacks) {
      // contanerInfo: container的dom节点
      // tag: 表示老的root, 还是新的同时更新的root节点
      // hydrate: ssr
      return createFiberRoot(containerInfo, tag, hydrate);
    }
    
    // 创建Fiber根节点
    function createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks) {
      //  这里创建的fiber的根节点
      var root = new FiberRootNode(containerInfo, tag, hydrate);
      // root上挂载container的信息
      // stateNode is any.
    
    
      // 这里才是创建真正的fiber
      var uninitializedFiber = createHostRootFiber(tag);
      root.current = uninitializedFiber;
      uninitializedFiber.stateNode = root;
      initializeUpdateQueue(uninitializedFiber);
      return root;
    }
    

    FiberRootNode

    function FiberRootNode(containerInfo, tag, hydrate) {
      this.tag = tag; // 表示老的root, 还是新的同时更新的root节点
      this.containerInfo = containerInfo; // container的dom节点
      this.pendingChildren = null;
      this.current = null;
      this.pingCache = null;
      this.finishedWork = null;
      this.timeoutHandle = noTimeout;
      this.context = null;
      this.pendingContext = null;
      this.hydrate = hydrate; // 是否ssr
      this.callbackNode = null;
      this.callbackPriority = NoLanePriority;
      this.eventTimes = createLaneMap(NoLanes);
      this.expirationTimes = createLaneMap(NoTimestamp);
      this.pendingLanes = NoLanes;
      this.suspendedLanes = NoLanes;
      this.pingedLanes = NoLanes;
      this.expiredLanes = NoLanes;
      this.mutableReadLanes = NoLanes;
      this.finishedLanes = NoLanes;
      this.entangledLanes = NoLanes;
      this.entanglements = createLaneMap(NoLanes);
    
      {
        this.mutableSourceEagerHydrationData = null;
      }
    
      {
        this.interactionThreadID = tracing.unstable_getThreadID();
        this.memoizedInteractions = new Set();
        this.pendingInteractionMap = new Map();
      }
    
      {
        switch (tag) {
          case BlockingRoot:
            this._debugRootType = 'createBlockingRoot()';
            break;
    
          case ConcurrentRoot:
            this._debugRootType = 'createRoot()';
            break;
    
          case LegacyRoot:
            this._debugRootType = 'createLegacyRoot()';
            break;
        }
      }
    }
    

    createLaneMap

    function createLaneMap(initial) {
      // Intentionally pushing one by one.
      // https://v8.dev/blog/elements-kinds#avoid-creating-holes
      var laneMap = [];
    
      for (var i = 0; i < TotalLanes; i++) {
        laneMap.push(initial);
      }
    
      return laneMap;
    }
    

    createHostRootFiber

    // 根据tab, 创建fiber的根节点
    function createHostRootFiber(tag) {
      var mode;
    
      if (tag === ConcurrentRoot) { // 根据tab指定mode
        mode = ConcurrentMode | BlockingMode | StrictMode;
      } else if (tag === BlockingRoot) {
        mode = BlockingMode | StrictMode;
      } else {
        mode = NoMode;
      }
    
      if ( 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);
    }
    
    var createFiber = function (tag, pendingProps, key, mode) {
      // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
      return new FiberNode(tag, pendingProps, key, mode);
    };
    

    FiberNode

    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.flags = NoFlags;
      this.nextEffect = null;
      this.firstEffect = null;
      this.lastEffect = null;
      this.lanes = NoLanes;
      this.childLanes = NoLanes;
      this.alternate = null;
    
      {
        // Note: The following is done to avoid a v8 performance cliff.
        //
        // Initializing the fields below to smis and later updating them with
        // double values will cause Fibers to end up having separate shapes.
        // This behavior/bug has something to do with Object.preventExtension().
        // Fortunately this only impacts DEV builds.
        // Unfortunately it makes React unusably slow for some applications.
        // To work around this, initialize the fields below with doubles.
        //
        // Learn more about this here:
        // https://github.com/facebook/react/issues/14365
        // https://bugs.chromium.org/p/v8/issues/detail?id=8538
        this.actualDuration = Number.NaN;
        this.actualStartTime = Number.NaN;
        this.selfBaseDuration = Number.NaN;
        this.treeBaseDuration = Number.NaN; // It's okay to replace the initial doubles with smis after initialization.
        // This won't trigger the performance cliff mentioned above,
        // and it simplifies other profiler code (including DevTools).
    
        this.actualDuration = 0;
        this.actualStartTime = -1;
        this.selfBaseDuration = 0;
        this.treeBaseDuration = 0;
      }
    
      {
        // This isn't directly used but is handy for debugging internals:
        this._debugID = debugCounter++;
        this._debugSource = null;
        this._debugOwner = null;
        this._debugNeedsRemount = false;
        this._debugHookTypes = null;
    
        if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') {
          Object.preventExtensions(this); // 让对象变得不能扩展
        }
      }
    }
    

    initializeUpdateQueue

    function initializeUpdateQueue(fiber) { // 初始化更新队列
      var queue = {
        baseState: fiber.memoizedState, // null
        firstBaseUpdate: null,
        lastBaseUpdate: null,
        shared: {
          pending: null
        },
        effects: null
      }; // 啥啥都是null, 把队列返回出去
      fiber.updateQueue = queue;
    }
    
  • 相关阅读:
    CentOS离线状态下安装Python3.7.0
    SQL练习题
    ajax的工作原理
    总结 XSS 与 CSRF 两种跨站攻击
    转css中文英文换行、禁止换行、显示省略号
    vue项目的搭建
    转axios 的应用
    rem布局计算(移动端,pc端有兼容性)
    rem
    彻底弄懂css中单位px和em,rem的区别
  • 原文地址:https://www.cnblogs.com/zhangrunhao/p/15680997.html
Copyright © 2011-2022 走看看