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

参考

相关推荐
拉不动的猪6 分钟前
前端自做埋点,我们应该要注意的几个问题
前端·javascript·面试
王景程16 分钟前
如何测试短信接口
java·服务器·前端
安冬的码畜日常39 分钟前
【AI 加持下的 Python 编程实战 2_10】DIY 拓展:从扫雷小游戏开发再探问题分解与 AI 代码调试能力(中)
开发语言·前端·人工智能·ai·扫雷游戏·ai辅助编程·辅助编程
小杨升级打怪中44 分钟前
前端面经-JS篇(三)--事件、性能优化、防抖与节流
前端·javascript·xss
清风细雨_林木木1 小时前
Vue开发网站会有“#”原因是前端路由使用了 Hash 模式
前端·vue.js·哈希算法
鸿蒙布道师1 小时前
OpenAI为何觊觎Chrome?AI时代浏览器争夺战背后的深层逻辑
前端·人工智能·chrome·深度学习·opencv·自然语言处理·chatgpt
袈裟和尚1 小时前
如何在安卓平板上下载安装Google Chrome【轻松安装】
前端·chrome·电脑
曹牧1 小时前
HTML字符实体和转义字符串
前端·html
小希爸爸1 小时前
2、中医基础入门和养生
前端·后端
局外人LZ1 小时前
前端项目搭建集锦:vite、vue、react、antd、vant、ts、sass、eslint、prettier、浏览器扩展,开箱即用,附带项目搭建教程
前端·vue.js·react.js