建立你自己的react(五)---Fibers

前言

自从react16.8.0版本出来fiber概念以后,这个就成为面试的重点,如果面试官问你fiber的相关问题,你是否能答得出来呢

Fibers

为了组织工作单元,我们将需要一个数据结构:一个fiber树

我们将每一个元素对应一个fiber,每一个fiber就是一个工作单元

让我们展示给你一个例子。

假设我们想要渲染一个像这样的元素树:

js 复制代码
Didact.render(
  <div>
    <h1>
      <p />
      <a />
    </h1>
    <h2 />
  </div>,
  container
)

在这个render中,我们将创建一个root fiber,然后设置他作为nextUnitOfWork. 剩余的工作将发生在performUnitOfWork函数, 我们将为每一个fiber做以下三件事情:

  1. 添加元素到DOM中
  2. 为每一个元素的子元素创建fiber
  3. 选择下一个工作单元

这个数据结构的目标之一是为了可以方柏霓的找到下一个工作单元。 这也就是为什么每一个fiber都与他的第一个孩子2,他的兄弟节点和他的父母有链接。

当我们完成了在每一个fiber上的performing work,如果它有一个子节点那么fiber将作为下一个工作单元。

从我们的例子来看,当我们完成了在div 这个fiber上的工作,那么下一个工作单元就是h1 fiber.

如果这个fiber没有一个子节点,我们会使用兄弟节点作为下一个工作单元。

例如,这个p fiber没有一个子节点,所以我们在完成它以后转移到a fiber.

如果一个fiber既没有子节点,也没有兄弟节点,那么我们就去找'叔叔':这个父母的兄弟,例如像a和h2这样的fibers

如果这个父节点没有兄弟节点,我们会继续这个父节点向上找,直到我们找到一个有兄弟的父节点,或者直到根节点。如果我们已经到达了根节点,那么也就意味着我们已经完成所有渲染的工作。

现在让我们来用代码实现。

首先,让我们移除这些代码从render函数中。

js 复制代码
function render(element, container) {
  const dom =
    element.type == "TEXT_ELEMENT"
      ? document.createTextNode("")
      : document.createElement(element.type)
​
  const isProperty = key => key !== "children"
  Object.keys(element.props)
    .filter(isProperty)
    .forEach(name => {
      dom[name] = element.props[name]
    })
​
  element.props.children.forEach(child =>
    render(child, dom)
  )
​
  container.appendChild(dom)
}

我们保留创建一个DOM节点这一块内容,我们稍后还会用到它。

在这个render函数中我们设置nextUnitOfWork为这个fiber树的根节点。

js 复制代码
function render(element, container) {
  nextUnitOfWork = {
    dom: container,
    props: {
      children: [element],
    },
  }
}

然后,当这个浏览器已经准备好的时候,它将调用我们的workLoop函数,我们也将从这个根部开始工作。

js 复制代码
function workLoop(deadline) {
  nextUnitOfWork = performUnitOfWork(
    nextUnitOfWork
  )
}

​
function performUnitOfWork(fiber) {
  // TODO 添加dom节点
  // TODO 创建一个新的fiber
  // TODO 返回下一个工作单元
}

首先,我们创建一个新节点,然后添加它到这个DOM中,我们保持这个DOM 节点的追踪,在这个fiber.dom属性中。

js 复制代码
function performUnitOfWork(fiber) {
  if (!fiber.dom) {
    fiber.dom = createDom(fiber)
  }
​
  if (fiber.parent) {
    fiber.parent.dom.appendChild(fiber.dom)
  }
​
  // TODO 创建一个新的fiber
  // TODO 返回下一个工作单元
}

然后为每一个子节点都创建一个新的fiber。

js 复制代码
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,
    }
  }

接下来我们添加它到我们的fiber树中,也设置它作为一个子节点或者作为一个兄弟节点,依赖于是否它是第一个子元素。

js 复制代码
if (index === 0) {
    fiber.child = newFiber
  } else {
    prevSibling.sibling = newFiber
  }
​
  prevSibling = newFiber
  index++

最终我们查找下一个工作节点。我们首先尝试他的子节点,然后是兄弟节点,然后是他的叔叔,等等。

js 复制代码
if (fiber.child) {
    return fiber.child
  }
  let nextFiber = fiber
  while (nextFiber) {
    if (nextFiber.sibling) {
      return nextFiber.sibling
    }
    nextFiber = nextFiber.parent
  }

这就是最终我们的performUnitOfWork 函数

js 复制代码
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
 }
}

参考

相关推荐
wordbaby4 分钟前
TanStack Router 基于文件的路由
前端
wordbaby8 分钟前
TanStack Router 路由概念
前端
wordbaby11 分钟前
TanStack Router 路由匹配
前端
cc蒲公英12 分钟前
vue nextTick和setTimeout区别
前端·javascript·vue.js
程序员刘禹锡16 分钟前
Html中常用的块标签!!!12.16日
前端·html
我血条子呢27 分钟前
【CSS】类似渐变色弯曲border
前端·css
DanyHope27 分钟前
LeetCode 两数之和:从 O (n²) 到 O (n),空间换时间的经典实践
前端·javascript·算法·leetcode·职场和发展
hgz071028 分钟前
企业级多项目部署与Tomcat运维实战
前端·firefox
用户18878710698429 分钟前
基于vant3的搜索选择组件
前端
zhoumeina9929 分钟前
懒加载图片
前端·javascript·vue.js