建立你自己的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
 }
}

参考

相关推荐
qiyi.sky1 分钟前
JavaWeb——Vue组件库Element(3/6):常见组件:Dialog对话框、Form表单(介绍、使用、实际效果)
前端·javascript·vue.js
煸橙干儿~~5 分钟前
分析JS Crash(进程崩溃)
java·前端·javascript
安冬的码畜日常14 分钟前
【D3.js in Action 3 精译_027】3.4 让 D3 数据适应屏幕(下)—— D3 分段比例尺的用法
前端·javascript·信息可视化·数据可视化·d3.js·d3比例尺·分段比例尺
l1x1n042 分钟前
No.3 笔记 | Web安全基础:Web1.0 - 3.0 发展史
前端·http·html
昨天;明天。今天。1 小时前
案例-任务清单
前端·javascript·css
zqx_72 小时前
随记 前端框架React的初步认识
前端·react.js·前端框架
惜.己2 小时前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
什么鬼昵称3 小时前
Pikachu-csrf-CSRF(get)
前端·csrf
长天一色3 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript
NiNg_1_2343 小时前
npm、yarn、pnpm之间的区别
前端·npm·node.js