从jsx->VDom->Fiber随笔

125 阅读4分钟

a. JSX → React Element(vDOM) :每次 render 都会生成新的 React Element 树(vDOM)。

b. React Element → Fiber:React 在协调阶段(Reconciliation)会将 React Element 转换为 Fiber 树,并在过程中进行 Diff

JSX 会被编译器(如 Babel)转换为 React.createElement() 调用。

function MyComponent() {
  return React.createElement(
    'div',
    null,
    React.createElement('h1', null, 'Hello, React!'),
    React.createElement('p', null, 'This is a paragraph.')
  );
}

React.createElement()

React.createElement() 是一个函数,用于创建虚拟 DOM 节点。它接受三个参数:元素类型、属性和子元素。

虚拟 DOM 节点

React.createElement() 返回一个 JavaScript 对象,表示虚拟 DOM 节点。

虚拟 DOM 树

组件返回的虚拟 DOM 节点会递归地构建一个虚拟 DOM 树。

Fiber 架构

Fiber 是 React 内部用于管理渲染工作的新架构。它允许 React 将渲染工作分割为可中断的块,从而提高性能并避免长时间阻塞主线程。

Fiber 节点

每个虚拟 DOM 节点对应一个 Fiber 节点。Fiber 节点包含额外的信息,用于管理渲染工作。 JavaScript

Fiber 树

Fiber 节点会构建一个 Fiber 树,用于表示组件的渲染状态。

工作循环(Work Loop)

React 使用一个工作循环来分阶段完成渲染工作。工作循环会根据浏览器的空闲时间来执行渲染任务。

单元工作(Unit of Work)

每个 Fiber 节点代表一个单元工作。React 会递归地处理每个单元工作。

提交阶段(Commit Phase)

在工作循环完成所有单元工作后,React 会进入提交阶段,将更新应用到 DOM。 React 会根据 Fiber 树的变化,生成一系列 DOM 操作。

从 JSX 到页面渲染的整个过程可以总结如下:

  1. JSX 转换:JSX 被编译器转换为 React.createElement() 调用。
  2. 虚拟 DOM 创建:React.createElement() 创建虚拟 DOM 节点,并构建虚拟 DOM 树。
  3. Fiber 构建:虚拟 DOM 树被转换为 Fiber 树,用于管理渲染工作。
  4. 工作循环:React 使用工作循环分阶段完成渲染任务。
  5. 提交阶段:将更新应用到 DOM,完成页面渲染。

虚拟DOM是包含DOM的一些信息,包括节点类型,props(id,children)这些简略的信息;

const virtualDOM = {
  type: 'div',
  props: {
    id:'666',
    children: [
      {
        type: 'h1',
        props: { children: 'Hello, React!' }
      },
      {
        type: 'p',
        props: { children: 'This is a paragraph.' }
      }
    ]
  }
};

diff算法主要流程:对比当前节点类型-相同则继续比较下一层级,不同则整个替换当前节点所有;比较子元素时:

- 如果元素类型不同,替换 DOM 节点。
- 如果元素类型相同,比较属性和子元素。
- 如果元素不存在于新列表中,移除对应的 DOM 节点。
- 如果元素在新列表中位置发生变化,移动 DOM 节点。

之前的虚拟DOM的diff计算和更新是通过递归计算的,这个过程不可以终止,树过于庞大时,存在长时间的计算,但是js线程和GUI线程是互斥的,会导致丢帧。

Fiber有一种退出机制,能将渲染的控制权让回去,

Fiber主要的优势
  • 可中断的异步渲染:将渲染任务拆分为多个,拆分到每一帧里面, Fiber 节点(链表结构),通过循环而非递归遍历,支持暂停、恢复和优先级调度。

  • 时间切片(Time Slicing) :利用浏览器空闲时间(使用requestIdleCallBack实现,允许在浏览器空闲时执行传递进去的回调函数)分批处理任务,避免长时间占用主线程,有就渲染,没有就暂停。

  • 优先级调度:高优先级任务(如用户交互)可打断低优先级任务(如数据渲染、网络请求),确保关键操作即时响应,React 的优先级调度由 Scheduler 模块 实现,核心是 过期时间(Expiration Time)  和 优先级车道(Lane)

  • Scheduler 给每个更新任务赋予优先级优先级高的更新任务A,会被推入 Reconciler,VDOM 转 Fiber,然后和旧的 Fiber 进行 diff 对比决定怎样生成新的 Fiber 树 。但如果此时有新的更高优先级的更新任务B 进入 Scheduler,那么 A 就会被中断B被推入 Reconciler,当 B 完成渲染后。新一轮的调度开始,A 是新一轮中优先级最高的,那 A 就继续推入 Reconciler 执行更新任务。重复以上的 可中断、可重复 步骤,直至所有更新任务完成渲染。分为render(Scheduler+Reconciler)和commit两个大的阶段。

fiber数据结构:

const fiber = { stateNode,// dom节点实例 
                child,// 当前节点所关联的子节点 
                sibling,// 当前节点所关联的兄弟节点 
                return// 当前节点所关联的父节点 }

清楚描述了节点关系,对于任务中断恢复有利,

React 更新DOM 采用的是双缓存技术。React 中最多会存在两颗 Fiber树:

  • currentFiber:页面中显示的内容
  • workInProgressFiber:内存中正在重新构建的 Fiber树。

双缓存中:当 workInProgressFiber 在内存中构建完成后,React 会直接用它 替换掉 currentFiber,这样能快速更新 DOM。一旦 workInProgressFiber树 渲染在页面上后,它就会变成 currentFiber 树,也就是说 fiberRootNode 会指向它。