任务实现统一提交功能
目前使用requestIdelCallback的问题
- 当渲染完ABC三个节点之后没有时间了,可能再两秒之后才有空闲时间才会去回调,那么对于用户来说就会卡顿了两秒之后,整个页面才会渲染完
-
用通俗的例子来讲,想象你帮妈妈在菜市场摆摊卖菜,妈妈说:「先把西红柿摆好,再把黄瓜摆好,最后把胡萝卜摆好。但如果有顾客来了,你必须马上停下来招呼他们!」(这就是
requestIdleCallback的逻辑——有顾客(其他任务)时就不干活)。结果你刚摆完西红柿,突然来了一群顾客!你只能停下手中的活去招呼他们。忙了两分钟后,顾客走了,你才赶紧继续摆黄瓜和胡萝卜。但这两分钟里,路过的客人看到摊位上只有孤零零的西红柿,会想:「这摊位怎么乱七八糟的?菜都没摆好呀!」
这就是电脑卡顿的原因——它总想等「完全没人打扰」的时候才继续干活,结果让画面看起来一直不完整。
- 目前代码,创建好了dom就添加到父级容器
function performWorkOfUnit(fiber) {
if (!fiber.dom) {
// 1. 创建dom
const dom = createDom(fiber.type)
fiber.dom = dom
// 添加到父级容器
fiber.parent.dom.append(dom)
// 2. 处理props
updateProps(dom, fiber.props)
}
initChildren(fiber)
// 4. 返回下一个任务
if (fiber.child) {
return fiber.child
}
if (fiber.sibling) {
return fiber.sibling
}
// 如果return undefined,则表示当前的树已经遍历完了
return fiber.parent?.sibling
}
解决办法,在最后的时候才把dom添加到容器里面
- 针对这个方案我需要知道两个点
- 处理完整个链表的时机,这个其实应在这段代码中,没有nextWorkOfUnit的时候
function workLoop(deadline) {
let shouldYield = false
while (!shouldYield && nextWorkOfUnit) {
// 返回下一个任务
nextWorkOfUnit = performWorkOfUnit(nextWorkOfUnit)
shouldYield = deadline.timeRemaining() < 1
}
if (!nextWorkOfUnit) {
console.log("render finished")
}
requestIdleCallback(workLoop)
}
- 第二点,我们需要知道根节点,我们需要把所有的节点递归的添加到父级容器中,记录下来
let root = null
function render(el, container) {
nextWorkOfUnit = {
dom: container,
props: {
children: [el]
}
}
root = nextWorkOfUnit
}
实现commitRoot,递归的去执行
function workLoop(deadline) {
let shouldYield = false
while (!shouldYield && nextWorkOfUnit) {
// 返回下一个任务
nextWorkOfUnit = performWorkOfUnit(nextWorkOfUnit)
shouldYield = deadline.timeRemaining() < 1
}
if (!nextWorkOfUnit) {
commitRoot()
}
requestIdleCallback(workLoop)
}
function commitRoot() {
commitWork(root.child)
}
function commitWork(fiber) {
if (!fiber) {
return
}
fiber.parent.dom.append(dom)
commitWork(fiber.child)
commitWork(fiber.sibling)
}
确保只会commit一次
function workLoop(deadline) {
let shouldYield = false
while (!shouldYield && nextWorkOfUnit) {
// 返回下一个任务
nextWorkOfUnit = performWorkOfUnit(nextWorkOfUnit)
shouldYield = deadline.timeRemaining() < 1
}
if (!nextWorkOfUnit && root) {
commitRoot()
}
requestIdleCallback(workLoop)
}
function commitRoot() {
commitWork(root.child)
root = null
}