1、Fiebr 作为数据结构
每个Fiber节点有个对应的React element,多个Fiber节点是如何连接形成树呢?
例如我们有这样一个属性结构:
react.render(<div><h1><p /><a /></h1><h2 /></div>,root)
React Fiber 机制的实现,是依赖下面这种数据结构(链表),每一个节点都是一个fiber。一个 fiber 包括了 child(第一个子节点)、sibling(兄弟节点)、parent(父节点)属性。
// 指向父级Fiber节点this.return = null;// 指向子Fiber节点this.child = null;// 指向右边第一个兄弟Fiber节点this.sibling = null;

2、构建过程
在构建中,我们将创建根fiber并将其设置为 nextUnitOfWork。剩下的工作将在 perforformunitofwork 功能上进行,每个fiber做了三件事:
- 将元素添加到 DOM
- 为元素的子元素创建fiber
- 选择下一个工作单元
实现目的是为了查找下一个工作单元变得更容易,使用的深度优先遍历,先查找子节点,在查找兄弟节点。
上图渲染过程详细描述如下:
- 当完成root的fiber工作时,如果有孩子,那么fiber是下一个工作的单元,root的子节点是div
- 当完成div的fiber工作时,下一个工作单元是h1
- h1的节点是p,继续下一个工作单元p
- p没有子节点,去找兄弟节点a
- a兄弟节点和子节点都没有,返回到父亲节点h1
- h1的子节点都已经工作完成了,去找h1的兄弟节点h2
- h2既没有兄弟节点,也没有子节点,返回到父亲节点div
- 同上,div在返回到父亲节点root
- 至此已经完成了所有的渲染工作
3、代码实现
1.抽离DOM节点的代码,放入到createDom()函数中;
/*** 创建DOM* @param {*} fiber fiber节点*/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}
2.在 render 函数中,设置nextUnitOfWork为fiber的根节点,根节点只包含一个children属性;
export function render(element, container) {// 将根节点设置为第一个将要工作单元nextUnitOfWork = {dom: container,props: {children: [element],},}}
3.当浏览器存在空闲时间,开始处理根节点;
/*** 处理工作单元,返回下一个单元事件* @param {*} nextUnitOfWork*/function performUnitOfWork(fiber) {// 如果fiber上没有dom节点,为其创建一个if (!fiber.dom) {fiber.dom = createDom(fiber)}// 如果fiber有父节点,将fiber.dom添加到父节点if (fiber.parent) {fiber.parent.dom.appendChild(fiber.dom)}}
3.为每一个孩子节点创建一个新的fiber;
function performUnitOfWork(fiber) {// 省略上面内容// 获取到当前fiber的孩子节点const elements = fiber.props.children// 索引let index = 0// 上一个兄弟节点let prevSibling = null// 遍历孩子节点while (index < elements.length) {const element = elements[index]// 创建fiberconst newFiber = {type: element.type,props: element.props,parent: fiber,dom: null,}}}
4.将新节点添加到fiber树中;
function performUnitOfWork(fiber) {// 省略上面内容while(index < elements.length){// 省略上面内容// 将第一个孩子节点设置为 fiber 的子节点if (index === 0) {fiber.child = newFiber} else if(element) {// 第一个之外的子节点设置为第一个子节点的兄弟节点prevSibling.sibling = newFiber}prevSibling = newFiberindex++}}
5.寻找下一个工作单元,先查找孩子,然后兄弟,如果没有就返回父节点;
function performUnitOfWork(fiber) {// 省略上面内容// 寻找下一个孩子节点,如果有返回if (fiber.child) {return fiber.child}let nextFiber = fiberwhile (nextFiber) {// 如果有兄弟节点,返回兄弟节点if (nextFiber.sibling) {return nextFiber.sibling}// 否则返回父节点nextFiber = nextFiber.parent}}
4、效果
修改src/index.js文件
实现效果:
