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 到页面渲染的整个过程可以总结如下:
- JSX 转换:JSX 被编译器转换为 React.createElement() 调用。
- 虚拟 DOM 创建:React.createElement() 创建虚拟 DOM 节点,并构建虚拟 DOM 树。
- Fiber 构建:虚拟 DOM 树被转换为 Fiber 树,用于管理渲染工作。
- 工作循环:React 使用工作循环分阶段完成渲染任务。
- 提交阶段:将更新应用到 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 会指向它。