React-fiber架构解析
什么是fiber
React中虚拟dom是对真实dom的一种简化,但是一些真实dom能做的事情,虚拟dom做不了,于是就有了fiber,fiber其实是指一种数据结构,有很多的属性,它可以用一个纯的JS对象来表示,借由fiber上的属性做到一些虚拟dom功能上的拓展
const fiber={
tag,
key,
type,
stateNode,
child,
subling,
return,
index,
memoizedState,
memoizedProps
pendingProps,
effectTag,
firstEffect,
lastEffect,
nextEffect,
alternate,
updateQueue,
}
一、fiber架构
以往版本不足:
界面DOM节点多,渲染比较耗时
React为了弥补之前版本的一些不足,设计了一些新的算法,由新算法设计出现了fiber这种数据结构
fiber数据结构+新的算法=fiber架构
JSX:React语法糖 描述UI界面
1.1、react应用从始至中管理这基本的三样东西
1、Root-- (应用根 一个对象 存在一个属性指向current树,一个属性指向workInProgress树 )
2、current树 (保存上一次状态的fiber树 ,且每个fiber节点都对应这一个jsx节点)
3、workInProgress树(保存每次新的状态的fiber树,并且每个fiber节点都对应一个jsx节点)
1.2、react初次渲染
1、react在第一次开始创建root时, 就会同时创建uninitialFiber对象 (未初始化的fiber)、
使react的current指向了uninitialFiber对象 假设为上一次状态
2、之后再去创建本次要用到的workInProgress树
二、Fiber 的主要工作流程:
ReactDOM.render() 引导 React 启动或调用 setState() 的时候开始创建或更新 Fiber 树。- 从根节点开始遍历 Fiber Node Tree, 并且构建 WokeInProgress Tree(reconciliation 阶段)。
- 本阶段可以暂停、终止、和重启,会导致 react 相关生命周期重复执行。
- React 会生成两棵树,一棵是代表当前状态的 current tree,一棵是待更新的 workInProgress tree。
- 遍历 current tree,重用或更新 Fiber Node 到 workInProgress tree,workInProgress tree 完成后会替换 current tree。
- 每更新一个节点,同时生成该节点对应的 Effect List。
- 为每个节点创建更新任务。
- 将创建的更新任务加入任务队列,等待调度。
- 调度由 scheduler 模块完成,其核心职责是执行回调。
- scheduler 模块实现了跨平台兼容的 requestIdleCallback。
- 每处理完一个 Fiber Node 的更新,可以中断、挂起,或恢复。
- 根据 Effect List 更新 DOM (commit 阶段)。
- React 会遍历 Effect List 将所有变更一次性更新到 DOM 上。
- 这一阶段的工作会导致用户可见的变化。因此该过程不可中断,必须一直执行直到更新完成。
三、React16版本之前的渲染方式
1、模拟render渲染
//作用:将元素渲染到界面
//参数:element 元素,rootPatent 渲染元素的根节点
let element =(
<div id="1">
I M hero
<div>哈哈哈</div>
</div>
)
function render(element,rootParent){
//1.创建元素
let dom=docment.createElement(element.type)
//2.给元素添加属性
Object.keys(element.props).filter(prop=>prop!="children")
.forEach(v=>dom[v]=element.props[v])
//3.将元素的子元素进行渲染(递归)
if(Array.isArray(element.props.children)){
element.props.children.forEach(c=>render(c,dom))
}else{
dom.innerHTML=element.props.children;
}
}
2、element 元素Balbel编译后的数据结构 ,传入render()供react渲染
{
"type": "div",
"key": null,
"ref": null,
"props": {
"id": "1",
"children": ["I M hero", {
"type": "div",
"key": null,
"ref": null,
"props": {
"children": "哈哈哈"
},
"_owner": null,
"_store": {}
}]
},
"_owner": null,
"_store": {}
}
3、 引发问题
因为如果界面节点多,层次深,递归渲染比较耗时
且 js是单线程的,UI线程和JS线程互斥,会照成界面卡顿,
所以 react在新版本中引入了 fiber的架构模式
4、老的架构方式造成界面掉帧卡顿原因
JS执行js引擎和页面渲染在同一个线程中,GUI渲染和JS执行两者是互斥的,如果某个js任务执行时间过长,浏览器就会推迟渲染,造成页面掉帧(浏览器每个帧都会进行样式计算、布局和绘制操作),页面刷新率低于24fps,形成视觉上的界面卡顿
2、fiber是一个执行单元,每次执行完一个执行单元,Reacta就会检查现在还剩多少时间,如果没有时间就将控制权让出去
四、关于setState是否为异步的解析
1、正常情况下,没有使用组件情况下,是同步更新的
但是不会立即获取到最新的state的值,因为这种情况调用setState只是单纯的将传入的state值放入UpdateQueen这条链表上,未执行更新
但内部会执行一个回调函数,才会真正的更新state,再重新渲染(无法立即获取值)
2、当使用组件时才是真正的异步更新模式,无法立即获取最新的状态,并且在更新和渲染时,会将整个过长放入eventloop中去执行 ,这时候才是真正的异步(无法立即获取值)
3、当使用flushSync()API书写setState时,react更新渲染完全同步,react会立即更新及渲染过程(可以立即获取值)
4、addEventLister()回调中setState(),非合成事件 (可以立即获取值)
5、setState()第二个参数回调函数中读取最新state值
五、fiber如何被打断
在reconciliation 阶段,指创建fiber的过程 :React16将Dom节点打散成了相互独立且有联系的一个个fiber对象,他们之间存在优先级关系,
在渲染时, 通过将一个个fiber任务打散在浏览器渲染的每一帧中 ,而Fiber 实现了自己的组件调用栈,它以链表的形式遍历组件树,可以灵活的暂停、继续和丢弃执行的任务。
? 实现方式是使用了浏览器的requestIdleCallback 这一 API,可以让浏览器在空闲的时候执行回调,在回调参数中可以获取到当前帧剩余的时间,fiber 利用了这个参数,判断当前剩下的时间是否足够继续执行任务,如果足够则继续执行,否则暂停任务,并调用 requestIdleCallback 通知浏览器下次空闲的时候继续执行当前的任务。
六、(React新版本中)fiber为什么要废弃一些生命周期
在 fiber 中,
更新分为两个阶段,
1、reconciliation(real爱可塞累神=>和解) 的阶段,这个阶段在计算前后 dom 树的差异,耗时长,可以被打断
2、然后是 commit 的阶段,这个阶段将把更新渲染到页面上,一口气把更新渲染到页面上,不会被打断
因此:
reconciliation 的阶段会被打断,可能会导致 commit 前的这些生命周期函数多次执行。react 官方目前已经把 componentWillMount 、componentWillReceiveProps 和 componetWillUpdate 标记为 unsafe,并使用新的生命周期函数 getDerivedStateFromProps 和 getSnapshotBeforeUpdate 进行替换。
mit 前的这些生命周期函数多次执行。react 官方目前已经把 componentWillMount 、componentWillReceiveProps 和 componetWillUpdate 标记为 unsafe,并使用新的生命周期函数 getDerivedStateFromProps 和 getSnapshotBeforeUpdate 进行替换。
|