Skip to content

Fibers ​

本节目标:理解“Fibers”的核心思路,并能把它用于实际开发或面试表达。

说明:原 OneNote 中有图片引用,但图片未包含在导出目录中;本页保留了可用的文字与代码内容。

为了组织工作单元,我们需要一个数据结:一个Fiber树。
We’ll have one fiber for each element and each fiber will be a unit of work.
让我举个例子。
假设我们想渲染一个这样的元素树:
==Didact====.====render====(==
==<div>==
==<h1>==
==<p />==
==<a />==
==</h1>==
==<h2 />==
==</div>,==
==container==
==)==

在渲染中,我们将创建根纤维并将其设置为下一个tunitofwork。剩下的工作将在performUnitOfWork功能上完成,在那里我们将为每根纤维做三件事:

==add the element to the DOM==
==create the fibers for the element’s children==
==select the next unit of work==

这种数据结构的目标之一是使查找下一个工作单元变得容易。这就是为什么每根纤维都与它的第一个子细胞、下一个兄弟细胞和亲本细胞相连。

当我们完成对一根纤维的工作时,如果它有一个孩子,那么纤维将是下一个工作单元。 在我们的示例中,当我们完成对div光纤的工作时,下一个工作单元将是h1光纤。

当我们完成对一根纤维的工作时,如果它有一个孩子,那么纤维将是下一个工作单元。 在我们的示例中,当我们完成对div光纤的工作时,下一个工作单元将是h1光纤。

如果纤维既没有孩子也没有兄弟姐妹,我们就去找“叔叔”:父母的兄弟姐妹。比如例子中的a和h2纤维。 同样,如果父结点没有兄弟结点,我们会一直向上,直到找到有兄弟结点的父结点,或者直到找到根结点。如果我们到达了根节点,这意味着我们已经完成了渲染的所有工作。

function createDom(fiber) {
    const dom =
        fiber.type == "TEXT_ELEMENT"
            ? document.createTextNode("")
            : document.createElement(fiber.type)
    const isProperty = key => key !== "children"
    Object.keys(fiber.props)
        .filter(isProperty)
        .forEach(name => {
            dom[name] = fiber.props[name]
        })
    return dom
}
In the ==render== function we set ==nextUnitOfWork== to the root of the fiber tree.
function render(element, container) {
    nextUnitOfWork = {
        dom: container,
        props: {
            children: [element],
        },
    }
}
let nextUnitOfWork = null
function workLoop(deadline) {
    let shouldYield = false
    while (nextUnitOfWork && !shouldYield) {
        nextUnitOfWork = performUnitOfWork(
            nextUnitOfWork
        )
        shouldYield = deadline.timeRemaining() < 1
    }
    requestIdleCallback(workLoop)
}
requestIdleCallback(workLoop)
function performUnitOfWork(fiber) {
    if (!fiber.dom) {
        fiber.dom = createDom(fiber)
    }
    if (fiber.parent) {
        fiber.parent.dom.appendChild(fiber.dom)
    }
    const elements = fiber.props.children
    let index = 0
    let prevSibling = null
    while (index < elements.length) {
        const element = elements[index]
        const newFiber = {
            type: element.type,
            props: element.props,
            parent: fiber,
            dom: null,
        }
        if (index === 0) {
            fiber.child = newFiber
        } else {
            prevSibling.sibling = newFiber
        }
        prevSibling = newFiber
        index++
    }
    if (fiber.child) {
        return fiber.child
    }
    let nextFiber = fiber
    while (nextFiber) {
        if (nextFiber.sibling) {
            return nextFiber.sibling
        }
        nextFiber = nextFiber.parent
    }
}

每次处理一个元素时,我们都会向DOM添加一个新节点。而且,请记住,浏览器可能会在我们完成渲染整个树之前中断我们的工作。在这种情况下,用户将看到一个不完整的UI。我们不想这样。

function commitRoot() {
    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)
}

本站总访问 — 次