Render and Commit Phases

接下来,还要再处理另一个问题

当我们对一个元素进行处理时,都会向 DOM 添加一个新的节点。注意,在我们完成整个树的渲染之前,浏览器是可能中断我们的工作的。此时,将会呈现一个不完整的用户页面,这是我们所不希望的。

所以,我们需要修改上一节实现的 performUnitOfWork 函数,移除其中改变 DOM 的部分

function performUnitOfWork(fiber) {
- if (fiber.parent) {
- fiber.parent.dom.appendChild(fiber.dom)
- }
}

此外,我们需要修改 render 函数,跟踪 fiber tree 的根节点。在此,称之为 工作进程的根(work in progress root)或 wipRoot

function render(element, container) {
- nextUnitOfWork = {
+ wipRoot = {
dom: container,
props: {
children: [element],
},
}
+ nextUnitOfWork = wipRoot
}
let nextUnitOfWork = null
+ let wipRoot = null

当没有下一个工作单元需要处理时,也就意味着完成了所有的工作,此时再将整个 fiber tree 提交至 DOM 上。

+ function commitRoot() {
+ // add nodes to dom
+ commitWork(wipRoot.child)
+ wipRoot = null
+ }
+
+ function commitWork(fiber) {
+ if (!fiber) {
+ return
+ }
+ const domParent = fiber.parent.dom
+ domParent.appendChild(fiber.dom)
+ commitWork(fiber.child)
+ commitWork(fiber.sibling)
+ }
function workLoop(deadline) {
let shouldYield = false
while (nextUnitOfWork && !shouldYield) {
nextUnitOfWork = performUnitOfWork(
nextUnitOfWork
)
shouldYield = deadline.timeRemaining() < 1
}
+ if (!nextUnitOfWork && wipRoot) {
+ commitRoot()
+ }
requestIdleCallback(workLoop)
}