React中虚拟dom是对真实dom的一种简化,但是一些真实dom能做的事情,虚拟dom做不了,于是就有了fiber,fiber其实是指一种数据结构,有很多的属性,它可以用一个纯的JS对象来表示,借由fiber上的属性做到一些虚拟dom功能上的拓展
//简化的fiber对象
const fiber={
tag,//fiber类型
key,
type,//标签类型
stateNode, //当前fiber实例
child, //子节点
subling, //兄弟节点
return, //父节点
index,
memoizedState,//当前fiber的state
memoizedProps//当前fiber的props
pendingProps,//传进来的新的props
effectTag,
//当前节点要进行何等更新 placement新插入 update更新 deletion删除 placementAndUpdate插入加更新 noWork当前节点没有任何工作
firstEffect,//当前有更新的第一个子节点
lastEffect,//当前节点有更新的最后一个子节点
nextEffect,//下一个要跟更新的子节点
alternate,//连接current和workInProgress树
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树
ReactDOM.render()
引导 React 启动或调用 setState()
的时候开始创建或更新 Fiber 树。1、模拟render渲染
//作用:将元素渲染到界面
//参数:element 元素,rootPatent 渲染元素的根节点
let element =(
I M hero
哈哈哈
)
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就会检查现在还剩多少时间,如果没有时间就将控制权让出去
1、正常情况下,没有使用组件情况下,是同步更新的
但是不会立即获取到最新的state的值,因为这种情况调用setState只是单纯的将传入的state值放入UpdateQueen这条链表上,未执行更新
但内部会执行一个回调函数,才会真正的更新state,再重新渲染(无法立即获取值)
2、当使用组件时才是真正的异步更新模式,无法立即获取最新的状态,并且在更新和渲染时,会将整个过长放入eventloop中去执行 ,这时候才是真正的异步(无法立即获取值)
3、当使用flushSync()API书写setState时,react更新渲染完全同步,react会立即更新及渲染过程(可以立即获取值)
4、addEventLister()回调中setState(),非合成事件 (可以立即获取值)
5、setState()第二个参数回调函数中读取最新state值
在reconciliation 阶段,指创建fiber的过程 :React16将Dom节点打散成了相互独立且有联系的一个个fiber对象,他们之间存在优先级关系,
在渲染时, 通过将一个个fiber任务打散在浏览器渲染的每一帧中 ,而Fiber
实现了自己的组件调用栈,它以链表的形式遍历组件树,可以灵活的暂停、继续和丢弃执行的任务。
实现方式是使用了浏览器的requestIdleCallback
这一 API,可以让浏览器在空闲的时候执行回调,在回调参数中可以获取到当前帧剩余的时间,fiber 利用了这个参数,判断当前剩下的时间是否足够继续执行任务,如果足够则继续执行,否则暂停任务,并调用 requestIdleCallback 通知浏览器下次空闲的时候继续执行当前的任务。
在 fiber 中,
更新分为两个阶段,
1、reconciliation(real爱可塞累神=>和解) 的阶段,这个阶段在计算前后 dom 树的差异,耗时长,可以被打断
2、然后是 commit 的阶段,这个阶段将把更新渲染到页面上,一口气把更新渲染到页面上,不会被打断
因此:
reconciliation 的阶段会被打断,可能会导致 commit 前的这些生命周期函数多次执行。react 官方目前已经把
componentWillMount
、componentWillReceiveProps
和componetWillUpdate
标记为 unsafe,并使用新的生命周期函数getDerivedStateFromProps
和getSnapshotBeforeUpdate
进行替换。
mit 前的这些生命周期函数多次执行。react 官方目前已经把 componentWillMount
、componentWillReceiveProps
和 componetWillUpdate
标记为 unsafe,并使用新的生命周期函数 getDerivedStateFromProps
和 getSnapshotBeforeUpdate
进行替换。