4. 实现 fiber 架构

66 阅读2分钟

目标: 实现fiber架构

如何控制dom树的渲染

  • 期望每一次渲染一个dom,实现完成之后检测还剩下多少空余时间,如果足够渲染另一个dom
  • 我们通过链表来实现,1. 找child节点 2. 找sibling节点 3. 找叔叔节点,具体规则可以参考下图。

2.png

实现代码的两种方案

  1. 先把树全部转成链表
  2. 可以边转链表,边去渲染,此方案更优,我们也是来实现此种方案,变构建关系,边渲染

把上节课中实现的任务调度器拷贝到react.js中和修改render

  • core/React.js
function workLoop(deadline) {
    let shouldYield = false
    while (!shouldYield && nextWorkOfUnit) {
        // 返回下一个任务
        nextWorkOfUnit = performWorkOfUnit(nextWorkOfUnit)
        shouldYield = deadline.timeRemaining() < 1
    }
    requestIdleCallback(workLoop)
}

requestIdleCallback(workLoop)

let nextWorkOfUnit = null

function render(el, container) {
    nextWorkOfUnit = {
        dom: container,
        props: {
            children: [el]
        }
    }
}

function performWorkOfUnit(work) {
    if (!work.dom) {
        // 1. 创建dom
        const dom = (work.dom = work.type === "TEXT_ELEMENT" ?
            document.createTextNode("") :
            document.createElement(work.type));
        
        // 添加到父级容器
        work.parent.dom.append(dom)
        // 2. 处理props 
        Object.keys(work.props).forEach((key) => {
            if (key !== "children") {
                dom[key] = work.props[key];
            }
        });
    }


    // 3. 转换链表 设置好指针
    const children = work.props.children
    let prevChild = null  
    children.forEach((child, index) => {
        // 创建一个新的对象表达parent,因为后需要找叔叔用到,不能直接加到child上会破坏vdom结构
        const newWork = {
            type: child.type,
            props: child.props, 
            child: null,
            parent: work,
            sibling: null,
            dom: null
        }
        if (index === 0) {
            work.child = newWork
        } else {
            prevChild.sibling = newWork  
        }
        prevChild = newWork
    })
    // 4. 返回下一个任务
    if (work.child) {
        return work.child
    }
    if (work.sibling) {
        return work.sibling
    }
    // 如果return undefined,则表示当前的树已经遍历完了
    return work.parent?.sibling
}

优化并重构封装代码,将work重命名成fiber

function workLoop(deadline) {
    let shouldYield = false
    while (!shouldYield && nextWorkOfUnit) {
        // 返回下一个任务
        nextWorkOfUnit = performWorkOfUnit(nextWorkOfUnit)
        shouldYield = deadline.timeRemaining() < 1
    }
    requestIdleCallback(workLoop)
}

function createDom(type) {
    return type === "TEXT_ELEMENT" ?
        document.createTextNode("") :
        document.createElement(type);
}

function updateProps(dom, props) {
    Object.keys(props).forEach((key) => {
        if (key !== "children") {
            dom[key] = props[key];
        }
    });
}

function initChildren(fiber) {
    // 3. 转换链表 设置好指针
    const children = fiber.props.children
    let prevChild = null
    children.forEach((child, index) => {
        // 创建一个新的对象表达parent,因为后需要找叔叔用到,不能直接加到child上会破坏vdom结构
        const newFiber = {
            type: child.type,
            props: child.props,
            child: null,
            parent: fiber,
            sibling: null,
            dom: null
        }
        if (index === 0) {
            fiber.child = newFiber
        } else {
            prevChild.sibling = newFiber
        }
        prevChild = newFiber
    }) 
}

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
}

requestIdleCallback(workLoop)