ReactDOM.render源码解析
上文提到fiberRoot和rootFiber, 它们到底是什么, 有什么关联?

在应用中ReactDOM.render可以被多次调用, 它们可以拥有不同的
rootFiber(FiberNode), 但是整个应用的根节点只能是一个, 那就是fiberRoot(fiberRootNode)从上图可以看出
fiberRoot(fiberRootNode)的current指向的是rootFiber(FiberNode), 也就是当前页面中所需要渲染的内容对应的Fiber树, 即 current fiber 树
它是整个应用的根节点, 绑定在
container._reactRootContainer, 也就是绑定在真实DOM节点的_reactRootContainer属性上, 就算是重复调用ReactDOM.render,FiberRoot(fiberRootNode)还是不变的, 内部做了判断. 详情可以看源码(源码地址)
export function createFiberRoot(
containerInfo: any,
tag: RootTag,
hydrate: boolean,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
): FiberRoot {
// 创建fiberRoot(fiberRootNode)
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.
const uninitializedFiber = createHostRootFiber(tag);// 创建rootFiber(fiberNode)
// 把rootFiber挂载到fiberRoot的current属性上
root.current = uninitializedFiber;
// 把fiberRoot挂载到rootFiber的stateNode属性上
uninitializedFiber.stateNode = root;
// 初始化更新队列
initializeUpdateQueue(uninitializedFiber);
return root;
}
这一段代码, 可以很好的解释上面fiberRoot和rootFiber关系图
type BaseFiberRootProperties = {|
// The type of root (legacy, batched, concurrent, etc.)
/*
export type RootTag = 0 | 1 | 2;
export const LegacyRoot = 0;
export const BlockingRoot = 1;
export const ConcurrentRoot = 2;
*/
tag: RootTag,
// root节点, 也就是ReactDOM.render( , document.getElementById('root'))的第二个参数
containerInfo: any,
pendingChildren: any,
//current:Fiber对象 对应的是 root 节点,即整个应用根对象
current: Fiber,
pingCache: WeakMap<Wakeable, Set<mixed>> | Map<Wakeable, Set<mixed>> | null,
// A finished work-in-progress HostRoot that's ready to be committed.
finishedWork: Fiber | null,
// Timeout handle returned by setTimeout. Used to cancel a pending timeout, if
// it's superseded by a new one.
timeoutHandle: TimeoutHandle | NoTimeout,
// Top context object, used by renderSubtreeIntoContainer
context: Object | null,
pendingContext: Object | null,
// Determines if we should attempt to hydrate on the initial mount
+hydrate: boolean,
// Used by useMutableSource hook to avoid tearing during hydration.
mutableSourceEagerHydrationData?: Array<
MutableSource<any> | MutableSourceVersion,
> | null,
// Node returned by Scheduler.scheduleCallback. Represents the next rendering
// task that the root will work on.
callbackNode: *,
callbackPriority: LanePriority,
eventTimes: LaneMap<number>,
expirationTimes: LaneMap<number>,
pendingLanes: Lanes,
suspendedLanes: Lanes,
pingedLanes: Lanes,
expiredLanes: Lanes,
mutableReadLanes: Lanes,
finishedLanes: Lanes,
entangledLanes: Lanes,
entanglements: LaneMap<Lanes>,
|};

rootFiber是所在组件树的根节点,rootFiber在每次重新渲染的时候会重新构建。
本身就是一个普通的fiberNode, 上面通过createHostRootFiber(tag)创建, 其实最终还是通过createFiber函数创建的.
export function createHostRootFiber(tag: RootTag): Fiber {
....
return createFiber(HostRoot, null, null, mode);
}
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
return new FiberNode(tag, pendingProps, key, mode);
};
function FiberNode(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
) {
// Instance
/**
export const FunctionComponent = 0;
export const ClassComponent = 1;
export const IndeterminateComponent = 2;
export const HostRoot = 3;
export const HostPortal = 4;
export const HostComponent = 5;
export const HostText = 6;
export const Fragment = 7;
*/
// tag 组件的类型 是函数组件, 类组件, Fragment, 原生的标签...
this.tag = tag;
this.key = key;
this.elementType = null;
// 对于 函数组件,指函数本身,对于类组件,指class
this.type = null;
// Fiber对应的真实DOM节点
this.stateNode = null;
// 以下属性用于连接其他Fiber节点形成Fiber树。
// 指向父fiber节点
this.return = null;
// 指向第一个子fiber节点
this.child = null;
// 指向第一个兄弟fiber节点
this.sibling = null;
this.index = 0;
// 真实的DOM属性
this.ref = null;
// 新传入的 props
this.pendingProps = pendingProps;
// 之前的 props
this.memoizedProps = null;
// 更新队列,用于暂存 setState 的值
this.updateQueue = null;
// 之前的 state
this.memoizedState = null;
this.dependencies = null;
this.mode = mode;
// 保存本次更新会造成的DOM操作。比如删除,移动
this.flags = NoFlags;
this.nextEffect = null;
this.firstEffect = null;
this.lastEffect = null;
this.lanes = NoLanes;
this.childLanes = NoLanes;
//用于链接新树和旧树;旧->新,新->旧
this.alternate = null;
}
stateNode: Fiber对应的真实DOM节点
查看stateNode, 测试代码:
function App(props) {
const content = <div className="app-div"></div>;
console.log(content);
return content;
}
ReactDOM.render(<App />, document.getElementById("root"));

alternate属性
第一个作用: 判断是首次渲染还是更新
第二个作用: 是当前更新的fiber tree(workInProgress tree) 和 current fiber tree(old tree)的桥梁
- 该属性在节点初始化的时候默认为空(this.alternate = null)。这个节点的作用就是用来缓存之前的 Fiber 节点,更新的时候会判断 fiber.alternate 是否为空来确定当前是首次渲染还是更新.
- react更新的时候其实存在两颗fiber树 [将产生update的React Element和current Fiber Tree(也就是old Tree)进行比较, 最终在内存中生成workInProgress tree], 我们使用alternate去连接old tree.

查看alternate, 测试代码:
function App(props) {
const [a, setA] = React.useState("");
const content = (
<div
className="app-div"
onClick={() => {
setA(Math.random());
}}
>
haha{a}
</div>
);
console.log(content); // 每次更新操作, 就打印出最新的fiber tree
return content;
}
ReactDOM.render(<App />, document.getElementById("root"));
