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...

相关推荐
Hyyy11 分钟前
ElementPlus按需加载 + 配置中文避坑(干掉1MB冗余代码)
前端·javascript·面试
Summer_Xu23 分钟前
模拟 Koa 中间件机制与洋葱模型
前端·设计模式·node.js
李鸿耀25 分钟前
📦 Rollup
前端·rollup.js
小kian27 分钟前
vite安全漏洞deny解决方案
前端·vite
时物留影29 分钟前
不写代码也能开发 API?试试这个组合!
前端·ai编程
试图感化富婆31 分钟前
【uni-app】市面上的模板一堆?打开源码一看乱的一匹?教你如何定制适合自己的模板
前端
卖报的小行家_31 分钟前
Vue3源码,响应式原理-数组
前端
牛马喜喜31 分钟前
如何从零实现一个todo list (2)
前端
小old弟36 分钟前
jQuery写油猴脚本报错eslint:no-undef - '$' is not defined
前端
Paramita36 分钟前
实战:使用Ollama + Node搭建本地AI问答应用
前端