5. 实现统一提交

61 阅读2分钟

任务实现统一提交功能

目前使用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
}
以上就是我们实现的统一提交的功能