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

参考

相关推荐
轻口味38 分钟前
【每日学点鸿蒙知识】AVCodec、SmartPerf工具、web组件加载、监听键盘的显示隐藏、Asset Store Kit
前端·华为·harmonyos
alikami40 分钟前
【若依】用 post 请求传 json 格式的数据下载文件
前端·javascript·json
wakangda1 小时前
React Native 集成原生Android功能
javascript·react native·react.js
吃杠碰小鸡1 小时前
lodash常用函数
前端·javascript
emoji1111111 小时前
前端对页面数据进行缓存
开发语言·前端·javascript
泰伦闲鱼1 小时前
nestjs:GET REQUEST 缓存问题
服务器·前端·缓存·node.js·nestjs
m0_748250032 小时前
Web 第一次作业 初探html 使用VSCode工具开发
前端·html
一个处女座的程序猿O(∩_∩)O2 小时前
vue3 如何使用 mounted
前端·javascript·vue.js
m0_748235952 小时前
web复习(三)
前端
AiFlutter2 小时前
Flutter-底部分享弹窗(showModalBottomSheet)
java·前端·flutter