Open Cosen95 opened 4 years ago
FiberRoot
RootFiber
接下来上面的继续分析legacyCreateRootFromDOMContainer方法中的剩余内容,在函数体的结尾返回了调用createLegacyRoot方法返回的一个ReactSyncRoot实例。来看createLegacyRoot方法的定义(packages/react-dom/src/client/ReactDOMRoot.js):
legacyCreateRootFromDOMContainer
createLegacyRoot
ReactSyncRoot
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:
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方法的定义:
createRootImpl
_internalRoot
/** * 创建并返回一个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:
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:
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构造函数的实现(这里只列举部分属性):
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:
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:
createFiber
/** * 创建并返回一个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
DOM
fiber tree
在目前阶段,我们暂时不用关心一个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; ... }
在本小节中我们主要是为了理解FiberRoot和RootFiber这两个容易混淆的概念以及两者之间的联系。同时在这里我们需要特别注意的是,多个fiber节点可形成基于单链表的树形结构,通过自身的return,child和sibling属性可以在多个fiber节点之间建立联系。
return
child
sibling
FiberRoot
和RootFiber
接下来上面的继续分析
legacyCreateRootFromDOMContainer
方法中的剩余内容,在函数体的结尾返回了调用createLegacyRoot
方法返回的一个ReactSyncRoot
实例。来看createLegacyRoot
方法的定义(packages/react-dom/src/client/ReactDOMRoot.js
):内部又实例化了
ReactDOMBlockingRoot
:内部调用了
createRootImpl
方法,并将返回结果赋值给_internalRoot
,来看createRootImpl
方法的定义:从上述源码中,我们可以看到
createRootImpl
方法通过调用createContainer
方法来创建一个fiberRoot
实例,并将该实例返回并赋值到ReactSyncRoot
构造函数的内部成员_internalRoot
属性上。我们继续深入createContainer
方法去探究一下fiberRoot
完整的创建过程,该方法被抽取到与react-dom
包同级的另一个相关的依赖包react-reconciler
包中,然后定位到packages/react-reconciler/src/ReactFiberReconciler.new.js
:内部调用了
createFiberRoot
方法,定义在packages/react-reconciler/src/ReactFiberRoot.new.js
:一个完整的
FiberRootNode
实例包含了很多有用的属性,这些属性在任务调度阶段都发挥着各自的作用,可以看到完整的FiberRootNode
构造函数的实现(这里只列举部分属性):在了解完了
fiberRoot
的属性结构之后,接下来继续探究createFiberRoot
方法的后半部分内容:看下
createHostRootFiber
,定义在packages/react-reconciler/src/ReactFiber.new.js
:内部调用了
createFiber
,定义在packages/react-reconciler/src/ReactFiber.new.js
:至此我们就成功地创建了一个
fiber
节点,上文中我们提到过,和DOM
树结构类似,fiber
节点也会形成一个与DOM
树结构对应的fiber tree
,并且是基于单链表的树结构,我们在上面刚创建的fiber
节点可作为整个fiber tree
的根节点,即RootFiber
节点。在目前阶段,我们暂时不用关心一个
fiber
节点所包含的所有属性,但可以稍微留意一下以下相关属性:在本小节中我们主要是为了理解
FiberRoot
和RootFiber
这两个容易混淆的概念以及两者之间的联系。同时在这里我们需要特别注意的是,多个fiber
节点可形成基于单链表的树形结构,通过自身的return
,child
和sibling
属性可以在多个fiber
节点之间建立联系。