话不多说,直接上代码。
入口函数 createRoot
// 第一个参数 domNode 取值 Document、Element 或者 DocumentFragment
// 第二个参数 options 可选,参考 https://zh-hans.react.dev/reference/react-dom/client/createRoot
createRoot(domNode, options?){
const root: FiberRoot = createContainer(params)
// 标记 container 是根 Fiber
// 这个函数给 container 根 DOM 节点赋值根 Fiber
markContainerAsRoot(root.current, container)
return new ReactDOMRoot(root)
}
进入 createRoot
函数体。
createContainer
先说下调用路线
createContainer
调用createFiberRoot
createFiberRoot
调用createHostRootFiber
和initializeUpdateQueue
createHostRootFiber
调用createFiber
createFiber
调用FiberNode
对应下面 4 段代码。
// 参数透传给 createFiberRoot
function createContainer(params) {
createFiberRoot(params)
}
function FiberRootNode() {
//初始化 FiberRoot
this.current = null
this.next = null
...
}
type SharedQueue = {
pending: Update<State> | null, // 单向循环链表
...
}
type UpdateQueue<State> = {
baseState: State,
// 单链表 firstBaseUpdate -> ... -> lastBaseUpdate
firstBaseUpdate: Update<State> | null,
// React 中更喜欢链表结构
// 一般情况下,单链表是不用记录尾节点,这里记录尾节点是为了快速比较两个单链表,用尾节点比较
lastBaseUpdate: Update<State> | null,
shared: SharedQueue<State>
}
// 初次渲染页面和类组件初次挂载的时候,调用函数 initializeUpdateQueue 来初始化 fiber.updateQueue
// 这里初始化 fiber.updateQueue。在 beginWork 阶段,updateHostRoot 中使用 processUpdateQueue 函数再具体赋值
function initializeUpdateQueue<State>(fiber: Fiber): void {
// 初始化 queue
// 一个 fiber 上会有多个 update,存起来后面批量更新
const queue: UpdateQueue<State> = {
baseState: fiber.memoizedState, // 初次渲染,存的是 Element;类组件,存的是初始的状态值
// 单向循环链表
firstBaseUpdate: null,
laseBaseUpdate: null,
shared: {
pending: null,
lanes: NoLanes,
hiddenCallbacks: null
},
callbacks: null
}
fiber.updateQueue = queue
}
// FiberRoot 是 rootFiber 的类型
// React 中有两种 fiber,普通 fiber 对应类型 Fiber,根 Fiber 对应类型 FiberRoot
function createFiberRoot(): FiberRoot {
const root: FiberRoot = new FiberRootNode()
const uninitializedFiber = createHostRootFiber()
// 循环构造
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;
initializeUpdateQueue(uninitializedFiber)
return root
}
function createHostRootFiber(params):Fiber {
createFiber(params)
}
function FiberNode() {
this.ref = null
this.flag = NoFlags
this.alternate = null
}
function createFiber() {
new FiberNode()
}
markContainerAsRoot
顾名思义,标记 Container 为根 Fiber。
const randomKey = Math.random().toString(36).slice(2) // 比如 xdzhvpd522c,其中 36 进制即 0-9 和 26 个英文字母
const internalContainerInstanceKey = '__reactContainer$' + randomKey
// 标记根节点
markContainerAsRoot(hostRoot: Fiber, node: Container) {
node[internalContainerInstanceKey] = hostRoot
}
这个属性值在函数 getClosestInstanceFromNode
和 getInstanceFromNode
中会用于根据 DOM 取 Fiber 值。
对应的还有两个函数:
// 取消标记,在 ReactDOMRoot.prototype.unmount 函数里调用
export function unmarkContainerAsRoot(node: Container): void {
node[internalContainerInstanceKey] = null
}
// 检查是否被标记为根节点
export function isContainerMarkedAsRoot(node: Container): boolean {
return !!node[internalContainerInstanceKey]
}
ReactDOMRoot
把 FiberRoot
放到内部参数 _internalRoot
上。
带
_internal
前缀的是 React 内部的参数。
function ReactDOMRoot(internalRoot: FiberRoot) {
this._internalRoot = internalRoot;
}