mini-react - 实现fiber架构

Fiber架构是一种软件架构,主要用于构建和运行Web应用程序 。它由Facebook开发,是一种基于JavaScript的框架。Fiber架构的核心思想是通过将应用程序逻辑划分为多个小单元(称为"Fiber"),然后在不同的线程或事件循环中执行这些单元,从而提高应用程序的性能和响应速度

在Fiber架构中,每个Fiber代表一个工作单元,可能是一个组件、一个函数或者是一个异步任务。Fiber通过一个调度器来管理这些工作单元的执行。调度器会根据优先级和依赖关系来安排Fiber的执行顺序。当一个Fiber执行完成后,它会被放入一个队列中,等待下一次执行。

Fiber架构的优势在于它可以充分利用现代硬件的多核特性,通过在不同的线程或事件循环中执行不同的Fiber,从而提高应用程序的并发性和性能。同时,Fiber架构还提供了更好的错误处理和调试支持,因为它可以轻松地跟踪和调试每个Fiber的执行过程。

任务调度器把大任务拆分到多个task里面完成,来解决dom树太多一次性加载导致的性能问题。

问题:

如何做到每次只渲染少数几个节点?

在下次执行的时候依然从之前的位置执行?

如何控制树的渲染

以上两个问题可以总结为,我们应该如何控制这颗树的渲染?ok, 我们的想法是:

现在分Task来进行,当一个Task渲染完了,如果还有空闲时间,则会继续下一个;这种一次进行处理的结构可以采用链表结构 。所以现在首先需要做的是,如何将这颗树转化为一个链表结构? 要将这颗树转化为一个链表结构,首先需要知道遍历规则,先遍历谁,接下来又是谁,所以首先需要构建遍历规则。

我们采用"一边构建链表关系,一边渲染dom"

如何控制树的渲染

构建遍历规则

遍历规则从根节点开始,从左到右,每次只构建当前节点和它的孩子节点之间关系

遍历顺序如下:

从root节点开始,找它的子节点,然后是兄弟节点;如果没有兄弟节点就找叔叔节点,知道既没有兄弟节点,也没有叔叔节点就表示遍历完成,形成了一个链表;

结合任务调度器实现执行逻辑

javascript 复制代码
/**
  * 1. 创建dom
  * 2. 处理props
  * 3. 转换链表,设置指针
  * 4. 返回下一个要执行的指针
 */

// 记录下一次任务
let nextWorkOFUnit = false
function workLoop(dleDeadline){
  // 状态 - 表示是否有空闲时间
  let shouldYield = false
  // nextWorkOFUnit 有任务的时候才去执行
  while(!shouldYield && nextWorkOFUnit){
    nextWorkOFUnit = preformWorkOfUnit(nextWorkOFUnit)
    shouldYield = dleDeadline.timeRemaining() < 1
  }
  requestIdleCallback(workLoop)
}
requestIdleCallback(workLoop)

//实现preformWorkOfUnit

function preformWorkOfUnit(work){
  // 1. 创建dom
  // 如果 work 上本身就有了dom,那么就无需去创建处理
  if(!work.dom){
    const dom = (work.dom = work.type === "TEXT_ELEMENT" ? document.createTextNode("") : document.createElement(work.type))

    // 需要将当前的dom 挂载到父节点上
    work.parent.dom.append(dom)

    // 2.处理props
    Object.keys(work.props).forEach(key => {
      if (key !== "children") {
        dom[key] = el.props[key]
      }
    })
  }

  // 3. 转换链表,设置指针
  const childrens = work.props.children
  let prevChild = null
  childrens.forEach((child,index)=>{
    // 因为有挂载 child 和 sibling 那么虚拟dom必然没有这两个属性,不能去破坏虚拟dom的结构
    const newWork = {
      type: child.type,
      props: child.props,
      child: null,
      parent: work,
      sibling: null,
      dom: null
    }
    // 先取第一个子节点 挂载到当前执行任务的子节点上
    if(index === 0){
      work.child = newWork
    }else{
      // 否则就是兄弟节点
      prevChild.sibling = newWork
    }
    prevChild = newWork
  })

  // 4. 返回下一个要执行的指针
  if(work.child){
    return  work.child
  }
  // 然后检查是否有兄弟节点
  if(work.sibling) {
    return work.sibling
  }
  // 如果没有,那么就找到parent ,然后去找叔叔节点
  return work.parent?.sibling
}

github.com/HolinWang/m...

相关推荐
夏幻灵32 分钟前
HTML5里最常用的十大标签
前端·html·html5
Mr Xu_1 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js
未来龙皇小蓝1 小时前
RBAC前端架构-01:项目初始化
前端·架构
程序员agions1 小时前
2026年,微前端终于“死“了
前端·状态模式
万岳科技系统开发1 小时前
食堂采购系统源码库存扣减算法与并发控制实现详解
java·前端·数据库·算法
程序员猫哥_1 小时前
HTML 生成网页工具推荐:从手写代码到 AI 自动生成网页的进化路径
前端·人工智能·html
龙飞051 小时前
Systemd -systemctl - journalctl 速查表:服务管理 + 日志排障
linux·运维·前端·chrome·systemctl·journalctl
我爱加班、、1 小时前
Websocket能携带token过去后端吗
前端·后端·websocket
AAA阿giao1 小时前
从零拆解一个 React + TypeScript 的 TodoList:模块化、数据流与工程实践
前端·react.js·ui·typescript·前端框架
杨超越luckly1 小时前
HTML应用指南:利用GET请求获取中国500强企业名单,揭秘企业增长、分化与转型的新常态
前端·数据库·html·可视化·中国500强