Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FiberRoot和RootFiber #53

Open
Cosen95 opened this issue Oct 27, 2020 · 0 comments
Open

FiberRoot和RootFiber #53

Cosen95 opened this issue Oct 27, 2020 · 0 comments

Comments

@Cosen95
Copy link
Owner

Cosen95 commented Oct 27, 2020

FiberRootRootFiber

接下来上面的继续分析legacyCreateRootFromDOMContainer方法中的剩余内容,在函数体的结尾返回了调用createLegacyRoot方法返回的一个ReactSyncRoot实例。来看createLegacyRoot方法的定义(packages/react-dom/src/client/ReactDOMRoot.js):

// packages/react-dom/src/client/ReactDOMRoot.js
export function createLegacyRoot(
  container: Container,
  options?: RootOptions
): RootType {
  return new ReactDOMBlockingRoot(container, LegacyRoot, options);
}

内部又实例化了ReactDOMBlockingRoot

// packages/react-dom/src/client/ReactDOMRoot.js
/**
 * ReactSyncRoot构造函数
 *
 * @param {Container} container DOM容器
 * @param {RootTag} tag fiberRoot节点的标记(LegacyRoot、BatchedRoot、ConcurrentRoot)
 * @param {(void | RootOptions)} options 配置信息,只有在hydrate时才有值,否则为undefined
 */
function ReactDOMBlockingRoot(
  container: Container,
  tag: RootTag,
  options: void | RootOptions
) {
  this._internalRoot = createRootImpl(container, tag, options);
}

内部调用了createRootImpl方法,并将返回结果赋值给_internalRoot,来看createRootImpl方法的定义:

/**
 * 创建并返回一个fiberRoot
 *
 * @param {Container} container DOM容器
 * @param {RootTag} tag fiberRoot节点的标记(LegacyRoot、BatchedRoot、ConcurrentRoot)
 * @param {(void | RootOptions)} options 配置信息,只有在hydrate时才有值,否则为undefined
 * @returns
 */
function createRootImpl(
  container: Container,
  tag: RootTag,
  options: void | RootOptions
) {
  // Tag is either LegacyRoot or Concurrent Root
  // 判断是否为hydrate模式
  const hydrate = options != null && options.hydrate === true;
  const hydrationCallbacks =
    (options != null && options.hydrationOptions) || null;
  const mutableSources =
    (options != null &&
      options.hydrationOptions != null &&
      options.hydrationOptions.mutableSources) ||
    null;
  // 创建一个fiberRoot
  const root = createContainer(container, tag, hydrate, hydrationCallbacks);
  // 给container附加一个内部属性用于指向fiberRoot的current属性对应的rootFiber节点
  markContainerAsRoot(root.current, container);
  const containerNodeType = container.nodeType;

  if (enableEagerRootListeners) {
    const rootContainerElement =
      container.nodeType === COMMENT_NODE ? container.parentNode : container;
    listenToAllSupportedEvents(rootContainerElement);
  } else {
    if (hydrate && tag !== LegacyRoot) {
      const doc =
        containerNodeType === DOCUMENT_NODE
          ? container
          : container.ownerDocument;
      // We need to cast this because Flow doesn't work
      // with the hoisted containerNodeType. If we inline
      // it, then Flow doesn't complain. We intentionally
      // hoist it to reduce code-size.
      eagerlyTrapReplayableEvents(container, ((doc: any): Document));
    } else if (
      containerNodeType !== DOCUMENT_FRAGMENT_NODE &&
      containerNodeType !== DOCUMENT_NODE
    ) {
      ensureListeningTo(container, "onMouseEnter", null);
    }
  }

  if (mutableSources) {
    for (let i = 0; i < mutableSources.length; i++) {
      const mutableSource = mutableSources[i];
      registerMutableSourceForHydration(root, mutableSource);
    }
  }

  return root;
}

从上述源码中,我们可以看到createRootImpl方法通过调用createContainer方法来创建一个fiberRoot实例,并将该实例返回并赋值到ReactSyncRoot构造函数的内部成员_internalRoot属性上。我们继续深入createContainer方法去探究一下fiberRoot完整的创建过程,该方法被抽取到与react-dom包同级的另一个相关的依赖包react-reconciler包中,然后定位到packages/react-reconciler/src/ReactFiberReconciler.new.js

/**
 * 内部调用createFiberRoot方法返回一个fiberRoot实例
 *
 * @export
 * @param {Container} containerInfo DOM容器
 * @param {RootTag} tag fiberRoot节点的标记(LegacyRoot、BatchedRoot、ConcurrentRoot)
 * @param {boolean} hydrate 判断是否是hydrate模式
 * @param {(null | SuspenseHydrationCallbacks)} hydrationCallbacks 只有在hydrate模式时才可能有值,该对象包含两个可选的方法:onHydrated和onDeleted
 * @returns {OpaqueRoot}
 */
export function createContainer(
  containerInfo: Container,
  tag: RootTag,
  hydrate: boolean,
  hydrationCallbacks: null | SuspenseHydrationCallbacks
): OpaqueRoot {
  return createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks);
}

内部调用了createFiberRoot方法,定义在packages/react-reconciler/src/ReactFiberRoot.new.js:

/**
 * 创建fiberRoot和rootFiber并相互引用
 *
 * @export
 * @param {*} containerInfo DOM容器
 * @param {RootTag} tag fiberRoot节点的标记(LegacyRoot、BatchedRoot、ConcurrentRoot)
 * @param {boolean} hydrate 判断是否是hydrate模式
 * @param {(null | SuspenseHydrationCallbacks)} hydrationCallbacks 只有在hydrate模式时才可能有值,该对象包含两个可选的方法:onHydrated和onDeleted
 * @returns {FiberRoot}
 */
export function createFiberRoot(
  containerInfo: any,
  tag: RootTag,
  hydrate: boolean,
  hydrationCallbacks: null | SuspenseHydrationCallbacks
): FiberRoot {
  // 通过FiberRootNode构造函数创建一个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.
  // 通过createHostRootFiber方法创建fiber tree的根结点,即rootFiber
  // fiber节点也会像DOM树结构一样形成一个fiber tree单链表树结构
  // 每个DOM节点或者组件都会生成一个与之对应的fiber节点,在后续的调和(reconciliation)阶段起着至关重要的作用
  const uninitializedFiber = createHostRootFiber(tag);
  // 创建完rootFiber之后,会将fiberRoot的实例的current属性指向刚创建的rootFiber
  root.current = uninitializedFiber;
  // 同时rootFiber的stateNode属性会指向fiberRoot实例,形成相互引用
  uninitializedFiber.stateNode = root;

  initializeUpdateQueue(uninitializedFiber);
  // 最后将创建的fiberRoot实例返回
  return root;
}

一个完整的FiberRootNode实例包含了很多有用的属性,这些属性在任务调度阶段都发挥着各自的作用,可以看到完整的FiberRootNode构造函数的实现(这里只列举部分属性):

// packages/react-reconciler/src/ReactFiberRoot.new.js
/**
 *  FiberRootNode构造函数
 * @param containerInfo DOM容器
 * @param tag fiberRoot节点的标记(LegacyRoot、BatchedRoot、ConcurrentRoot)
 * @param hydrate 判断是否是hydrate模式
 * @constructor
 */
function FiberRootNode(containerInfo, tag, hydrate) {
  // 用于标记fiberRoot的类型
  this.tag = tag;
  // 和fiberRoot关联的DOM容器的相关信息
  this.containerInfo = containerInfo;
  // 指向当前激活的与之对应的rootFiber节点
  this.current = null;

  ...
  // 当前的fiberRoot是否处于hydrate模式
  this.hydrate = hydrate;
  ...
  // 每个fiberRoot实例上都只会维护一个任务,该任务保存在callbackNode属性中
  this.callbackNode = null;
  // 当前任务的优先级
  this.callbackPriority = NoPriority;
  ...
}

在了解完了fiberRoot的属性结构之后,接下来继续探究createFiberRoot方法的后半部分内容:

// packages/react-reconciler/src/ReactFiberRoot.new.js > createFiberRoot
// Cyclic construction. This cheats the type system right now because
// stateNode is any.
// 通过createHostRootFiber方法创建fiber tree的根结点,即rootFiber
// fiber节点也会像DOM树结构一样形成一个fiber tree单链表树结构
// 每个DOM节点或者组件都会生成一个与之对应的fiber节点,在后续的调和(reconciliation)阶段起着至关重要的作用
const uninitializedFiber = createHostRootFiber(tag);
// 创建完rootFiber之后,会将fiberRoot的实例的current属性指向刚创建的rootFiber
root.current = uninitializedFiber;
// 同时rootFiber的stateNode属性会指向fiberRoot实例,形成相互引用
uninitializedFiber.stateNode = root;

initializeUpdateQueue(uninitializedFiber);
// 最后将创建的fiberRoot实例返回
return root;

看下createHostRootFiber,定义在packages/react-reconciler/src/ReactFiber.new.js

/**
 * 内部调用createFiber方法创建一个FiberNode实例
 *
 * @export
 * @param {RootTag} tag fiberRoot节点的标记(LegacyRoot、BatchedRoot、ConcurrentRoot)
 * @returns {Fiber}
 */
export function createHostRootFiber(tag: RootTag): Fiber {
  // 根据fiberRoot的标记类型来动态设置rootFiber的mode属性
  let mode;
  // mode定义在packages/react-reconciler/src/ReactTypeOfMode.js
  // export const NoMode = 0b00000; => 0
  // export const StrictMode = 0b00001; => 1
  // export const BlockingMode = 0b00010; => 2
  // export const ConcurrentMode = 0b00100; => 4
  // export const ProfileMode = 0b01000; => 8
  // export const DebugTracingMode = 0b10000;
  if (tag === ConcurrentRoot) {
    mode = ConcurrentMode | BlockingMode | StrictMode;
  } else if (tag === BlockingRoot) {
    mode = BlockingMode | 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;
  }
  // 调用createFiber方法创建并返回一个FiberNode实例,HostRoot表示fiber tree的根节点
  return createFiber(HostRoot, null, null, mode);
}

内部调用了createFiber,定义在packages/react-reconciler/src/ReactFiber.new.js

/**
 * 创建并返回一个FiberNode实例
 * @param {*} tag 用于标记fiber节点的类型
 * @param {*} pendingProps 表示待处理的props数据
 * @param {*} key 用于唯一标识一个fiber节点(特别在一些列表数据结构中,一般会要求为每个DOM节点或组件加上额外的key属性,在后续的调和阶段会派上用场)
 * @param {*} mode 表示fiber节点的模式
 */
const createFiber = function (
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode
): Fiber {
  // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
  // FiberNode构造函数用于创建一个FiberNode实例,即一个fiber节点
  return new FiberNode(tag, pendingProps, key, mode);
};

至此我们就成功地创建了一个fiber节点,上文中我们提到过,和DOM树结构类似,fiber节点也会形成一个与DOM树结构对应的fiber tree,并且是基于单链表的树结构,我们在上面刚创建的fiber节点可作为整个fiber tree的根节点,即RootFiber节点。

在目前阶段,我们暂时不用关心一个fiber节点所包含的所有属性,但可以稍微留意一下以下相关属性:

/**
 * FiberNode构造函数
 * @param tag 用于标记fiber节点的类型
 * @param pendingProps 表示待处理的props数据
 * @param key 用于唯一标识一个fiber节点(特别在一些列表数据结构中,一般会要求为每个DOM节点或组件加上额外的key属性,在后续的调和阶段会派上用场)
 * @param mode 表示fiber节点的模式
 * @constructor
 */
function FiberNode(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  // Instance
  // 用于标记fiber节点的类型
  this.tag = tag;
  // 用于唯一标识一个fiber节点
  this.key = key;
  ...
  // 对于rootFiber节点而言,stateNode属性指向对应的fiberRoot节点
  // 对于child fiber节点而言,stateNode属性指向对应的组件实例
  this.stateNode = null;

  // Fiber
  // 以下属性创建单链表树结构
  // return属性始终指向父节点
  // child属性始终指向第一个子节点
  // sibling属性始终指向第一个兄弟节点
  this.return = null;
  this.child = null;
  this.sibling = null;
  // index属性表示当前fiber节点的索引
  this.index = 0;
  ...

  // 表示待处理的props数据
  this.pendingProps = pendingProps;
  // 表示之前已经存储的props数据
  this.memoizedProps = null;
  // 表示更新队列
  // 例如在常见的setState操作中
  // 其实会先将需要更新的数据存放到这里的updateQueue队列中用于后续调度
  this.updateQueue = null;
  // 表示之前已经存储的state数据
  this.memoizedState = null;
  ...

  // 表示fiber节点的模式
  this.mode = mode;

  // 用于指向另一个fiber节点
  // 这两个fiber节点使用alternate属性相互引用,形成双缓冲
  // alternate属性指向的fiber节点在任务调度中又称为workInProgress节点
  this.alternate = null;
  ...
}

在本小节中我们主要是为了理解FiberRootRootFiber这两个容易混淆的概念以及两者之间的联系。同时在这里我们需要特别注意的是,多个fiber节点可形成基于单链表的树形结构,通过自身的returnchildsibling属性可以在多个fiber节点之间建立联系。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant