react-fiber
虚拟dom在react16中称为fiber。
起源
在react15之前,协调器采用递归的方式来创建dom,递归的过程是不能中断的,如果组件书的层级很深,递归会占用线程很多时间,造成卡顿。为了解决这个问题,react16将递归的无法中断的更新重构为异步的可中断的更新。
fiber
- 作为架构来说,react15的协调器使用递归的方式执行,数据保存在递归调用栈中,被称为stack reconciler, 在react16中,被成为fiber reconciber。
- 作为静态的数据结构来说,每个fiber节点对应的react元素,保存了组件的类型,对应的dom节点等信息。
- 作为动态的工作单元来说,每个fiber节点保存了本次更新中该组件改变的状态,要执行的工作。
结构
每个fiber节点有对应的react element,多个fiber节点会形成树。
// 指向父级fiber节点
this.return = null
// 指向子fiber节点
this.child = null
// 指向右边的第一个兄弟fiber节点
this.sibling = null
双缓存fiber树
在内存中绘制当前帧动画,绘制完成之后用当前帧替换上一帧画面,省区了两帧交替间的计算时间,不会从白屏到出现画面的闪烁情况。在内存中构建并直接替换的技术称为双缓存。
在react中最多会存在两棵fiber树,当前屏幕上显示内容对应的fiber树称为current fiber,正在内存中构建的fiber树称为workinprogress fiber树
当前的fiver树中的fiber节点称为current fiber,workinprogress fiber树中的fiber节点称为workinProgress fiber,通过alternate属性链接。
currentFiber.alternate === workInProgressFiber;
workInProgressFiber.alternate === currentFiber;
react应用的根节点使current指针在不同的fiber树的rootfiber之间切换来完成current fiber树指向的切换。
即当workInProgress Fiber树构建完成交给Renderer渲染在页面上后,应用根节点的current指针指向workInProgress Fiber树,此时workInProgress Fiber树就变为current Fiber树。
每次状态更新都会产生新的workInProgress Fiber树,通过current与workInProgress的替换,完成DOM更新。
mount时
function App() {
//
}
ReactDom.render(<App />, document.getElementById("root"))
首次运行reactdom.render会创建fiberrootnode=> fiberroot和rootFiber。fiberrootnode是整个应用的根节点,rootfiber是<App />
所在组件树中的根节点。
调用多次reactDOM.render渲染不同的组件树,会拥有不同的rootfiber,但是应用的根节点只有一个,就是fiberrootnode。
fiberRootNode的current会指向当前页面上已渲染内容对应的fiber树(currentFiber)
首屏渲染,页面中没有挂载dom,所以fiberrootnode.current指向的rootFiber没有任何子fiber节点。
然后是渲染节点,根据组件返回的jsx在内存中一次构建fiber节点,并连接在一起构建fiber树,称为workinprogress fiber树。
在构建workInProgress Fiber树时会尝试复用current Fiber树中已有的Fiber节点内的属性,在首屏渲染时只有rootFiber存在对应的current fiber(即rootFiber.alternate)。
然后进行commit,将fiber树commit阶段渲染到页面
update时
当触发状态改变的时候,会开启新的render阶段,然后构建一棵新的workInprogress fiber树。
和mount一样,workinprogress fiber的创建是可以服用current fiber树对应的节点数据的。这个决定是否复用的过程,就是diff算法。
workInprogress fiber树在render阶段完成构建后,进入commit阶段渲染到页面上,渲染完毕后,workinprogress fiber树变成current fiber树