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

相关推荐
码事漫谈6 分钟前
解决 Anki 启动器下载错误的完整指南
前端
im_AMBER26 分钟前
Web 开发 27
前端·javascript·笔记·后端·学习·web
蓝胖子的多啦A梦1 小时前
低版本Chrome导致弹框无法滚动的解决方案
前端·css·html·chrome浏览器·版本不同造成问题·弹框页面无法滚动
玩代码1 小时前
vue项目安装chromedriver超时解决办法
前端·javascript·vue.js
訾博ZiBo1 小时前
React 状态管理中的循环更新陷阱与解决方案
前端
StarPrayers.1 小时前
旅行商问题(TSP)(2)(heuristics.py)(TSP 的两种贪心启发式算法实现)
前端·人工智能·python·算法·pycharm·启发式算法
一壶浊酒..2 小时前
ajax局部更新
前端·ajax·okhttp
DoraBigHead2 小时前
React 架构重生记:从递归地狱到时间切片
前端·javascript·react.js
彩旗工作室3 小时前
WordPress 本地开发环境完全指南:从零开始理解 Local by Flywhee
前端·wordpress·网站
iuuia3 小时前
02--CSS基础
前端·css