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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章