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

相关推荐
Jiaberrr3 小时前
前端实战:使用JS和Canvas实现运算图形验证码(uniapp、微信小程序同样可用)
前端·javascript·vue.js·微信小程序·uni-app
everyStudy3 小时前
JS中判断字符串中是否包含指定字符
开发语言·前端·javascript
城南云小白3 小时前
web基础+http协议+httpd详细配置
前端·网络协议·http
前端小趴菜、3 小时前
Web Worker 简单使用
前端
web_learning_3213 小时前
信息收集常用指令
前端·搜索引擎
tabzzz3 小时前
Webpack 概念速通:从入门到掌握构建工具的精髓
前端·webpack
200不是二百3 小时前
Vuex详解
前端·javascript·vue.js
滔滔不绝tao4 小时前
自动化测试常用函数
前端·css·html5
码爸4 小时前
flink doris批量sink
java·前端·flink
深情废杨杨4 小时前
前端vue-父传子
前端·javascript·vue.js