一、什么是React Fiber
我们都知道从React16.8开始引入了fiber架构。旧版的React的render过程是同步的,如果Render耗时较长,会阻塞页面的其他任务。新版本的Render过程拆分成多个异步任务(纤程),并且是可中断可恢复的,这就是React Fiber.

原理:Render过程主要工作是vdom diff算法找出两棵树的区别,本质上的树的遍历。在旧版本react,假设遍历发生了中断,虽然可以保留当下进行中节点的索引,下次继续时,我们的确可以继续遍历该节点下面的所有子节点,但是没有办法找到其父节点因为每个节点只有其子节点的指向。断点没有办法恢复,只能从头再来一遍。在新的fiber架构中,每个节点有三个指针:分别指向第一个子节点、下一个兄弟节点、父节点。这种数据结构就是fiberNode。


二、React双缓冲树
javascript
import React from 'react';
import ReactDom from 'react-dom';
class Home extends React.Component {
constructor(props) {
super(props);
this.state = { step: 0 };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({step: this.state.step + 1})
}
render() {
const { step } = this.state;
return (
<div id={step} onClick={this.handleClick}>
{step}
</div>
)
}
}
ReactDom.render(<Home/>, document.getElementById('root'))
渲染流程
- render阶段 构建workInProgress树,用于本次渲染
- render结束,commit前 更新容器的finishedWork指针,通知页面渲染
- commit结束 current树变成alternate树,workInProgress树变成current树,finishedWork树为空
第一次渲染
- render阶段 初始化工作:创建容器和root fiberNode, current指针指向root fiber

- 构建workInProgressi树,即将渲染到页面的vdom 除了child、sibling、return:指针,还有alternate它指向备用节点

- render阶段结束,commit阶段开始 将fiber树容器的finishedWork指针执行workInProgress树root节点

- commit阶段结束 current指向workInProgree树,finishedWork指向null

第二次渲染
- render阶段 在current树的基础上构建workInProgress树,current.alternate节点不为空,存在备用节点,直接复用。

current树的其他子节点的alternate都为null,各自创建workInProgress节点

-
render阶段结束,commit阶段开始
将fiber树容器的finishedWork指针指向workInProgress树root节点

-
commit阶段结束
current指向workInProgress树,finishedWork指向null

第三次渲染
-
render阶段
构建workInProgress树,所有fiber节点存在备用节点,直接复用,并将属性复制过去。

- render阶段结束,commit阶段开始 将fiber树容器的finishedWork指针执行workInProgress树root节点

-
commit阶段结束
current指向workInProgress树,finishedWork指向null

三、结论
通过上面三次渲染更新过程也可以看出,React在渲染时,会在current树和alternate树之间交替进行,倒来倒去。比如第四次渲染时,第二次渲染完成的alternate树又变成了current树,而第三次渲染完成的树又变成了alternate树。 那为什么React要复用备用的节点,而不是新创建一个呢?最大的原因是节省内存开销,通过复用引旧的备用节点,React不需要额外申请内存空间,在复用时可以直接将current fiber的属性复制到l旧的备用节点。