接下来,需要实现 render 函数,进行第一次挂载。
jsx
const element = (
<h1>
hello <span>test</span> children
</h1>
);
const root = createRoot(document.getElementById("root"));
root.render(element);
由之前的文章可知,root
是一个 ReactDOMRoot
实例。所以直接在 ReactDOMRoot
原型上添加 render
方法。
render
// 注意 children 为传入的虚拟DOM 即 element
ReactDOMRoot.prototype.render = function (children) {
// 这个 root 是 FiberRootNode 实例
const root = this._internalRoot;
// 更新容器
updateContainer(children, root);
};
再回忆一下 HostRootFiber
和 FiberRootNode
。

由于是初次渲染,所以第一次处理 HostRootFiber
的更新队列。
updateContainer
/**
* 更新容器 把虚拟DOM 变成真实DOM 插入容器
* @param {*} element 虚拟DOM
* @param {*} container 容器 FiberRootNode
* @returns
*/
export function updateContainer(element, container) {
// HostRootFiber 根 fiber
const current = container.current;
// 创建更新
const update = createUpdate(eventTime, lane);
// 需要更新的虚拟DOM
update.payload = { element };
// 添加更新
// 返回根节点
const root = enqueueUpdate(current, update);
scheduleUpdateOnFiber(root);
}
更新队列的结构
由 initialUpdateQueue
可以看出,HostRootFiber
的 updateQueue
的结构为一个对象,对象中有个 shared
属性, shared
中有个 pending
属性指向循环链表。
updateQueue
export function initialUpdateQueue(fiber) {
const queue = {
shared: {
pending: null, // 循环链接,指向链表中最后一个 update
},
};
fiber.updateQueue = queue;
}

由图可知,pending
永远指向循环链表中最后一个元素。由此可以实现 enqueueUpdate
函数。
enqueueUpdate
export function enqueueUpdate(fiber, update) {
const updateQueue = fiber.updateQueue;
const sharedQueue = updateQueue.shared;
const pending = sharedQueue.pending;
if (pending === null) {
// 第一次入队,指向自己
update.next = update;
} else {
// 当队列不为空,新入的更新指向第一个更新
update.next = pending.next;
// pending 指向链表末尾
pending.next = update;
}
// 指向最后一个 update
sharedQueue.pending = update;
// 找到根节点并返回
return markUpdateLaneFromFiberToRoot(fiber);
}
执行 enqueueUpdate
后便生成了更新队列。
需要注意的是,当前更新中有一个 payload
参数,值为虚拟 DOM。