React源码自顶向下-理论基础篇
React 设计理念
- 设计理念:快速响应
- 制约瓶颈:计算能力和网络延迟也就是 CPU 和 IO
- 解决方法:异步可中断更新和人机交互
主流浏览器的渲染是 60hz,也就是 1000ms/60hz = 16.6ms 浏览器刷新一次。在此过程中,浏览器要进行 JS 的执行 -> 样式布局 -> 样式绘制,如果在这一帧中这三个过程不能全部完成,就要出现掉帧的情况。
如何优化 CPU?
异步可中断更新:浏览器将一帧的一部分时间预留给 react,react 利用这部分时间来完成自己的工作,如果每个长任务超过了预留时间,react 会中断自己的工作,并将控制权交还给浏览器,等待下一帧到来 react 才继续之前被中断的工作。这样,浏览器在每一帧都有时间进行样式布局和样式绘制,这样就有效减少了掉帧的可能性。
如何优化 IO?
将人机 交互研究的结果整合到真实 UI 中 Concurrent 模式介绍
React 架构演进史
React15
- Reconciler 协调器(采用递归的方式执行,也叫 Stack Reconciler):通过 reconcile(diff 算法)决定哪些组件需要更新
-
Renderer 渲染器:将需要更新的组件渲染到真实 DOM 上,这里有以下渲染器:
- ReactDOM:渲染到浏览器,或者 SSR
- ReactNative:渲染 App 原生组件
- ReactTest:将组件渲染 成纯 JS 对象,一般用于测试
- ReactArt:将组件渲染到 Canvas 或者 SVG 上
React15 更新流程 是协调器和渲染器依次执行工作的,本来先更新的组件要比后更新的组件先变化,但由于整个过程都是同步的,所以看不出变化。
但是如果把同步更新变为异步可中断的更新,那中断之后,对于需要批量更新的组件,后更新的组件不会渲染到视图上,而先更新的组件已经渲染到了视图上,感觉就像一个 bug。
React16
- 新增 Scheduler 调度器:决定高优先的任务先进入协调器。
- Reconciler 协调器(基于 Fiber 节点实现,也叫 Fiber Reconciler):通过 reconcile(diff 算法)创建虚拟 DOM 树,给需要更新的组件打上
Update
标记,决定哪些组件需要批量
更新 - Renderer 渲染器:接收到通知,将被打了
Update
标记的组件,也就是将需要更新的组件渲染到真实 DOM 上
React16 的流程是 如果协调器中有任务正在 diff,但是调度器有更高优的任务进来,那刚才的任务就会中断执行,反而先执行高优的任务。
由于调度器和协调器都是在内存中工作的,所以即使有中断发生,用户也不会看到更新不完全的视图。
React 新架构-Fiber
代数效应
代数效应
是函数式编程中的概念,用于将副作用
从函数调用中分离。- react 通过
代数效应
来实现异步可中断的更新。
什么是 Fiber
- Fiber 纤程
- Process 进程
- Thread 线程
- Coroutine 协程
JS 已通过 Generator 实现了协程,为什么 React 还要自己实现一个 Fiber 纤程呢?
- Generator 具有传染性,如果一个函数变成了 generator,那么调用它的函数也会变成 generator,从而受到了影响。
-
最重要的一点是:Fiber 架构为了达到两个目的:
- 异步可中断并且继续
- 更新具有优先级,高优先级的更新可以打断低优先级的更新。generator 可以实现第一点,但是无法实现第二点。
Fiber 架构的原理
- 作为架构来说,之前 React15 的 Reconciler 采用递归的方式执行,数据保存在递归调用栈中,所以被称为 stack Reconciler。React16 的 Reconciler 基于 Fiber 节点实现,被称为 Fiber Reconciler。
function App() {
const [value, setValue] = useState(0)
return (
<div
onClick={() => {
setValue(value + 1)
}}
>
pipihua-{value}
</div>
)
}
React.render(<App />, document.getElementById("root"))
- 作为静态数据结构来说,每个 Fiber 节点对应了一个组件 React Element,保存了组件的相关信息,比如该组件的类型、对应的 Dom 节点等信息,这时的 Fiber 节点也就是我们所说的虚拟 Dom。
- 作为动态工作单元来说,每个 Fiber 节点保存了本次更新相关的信息
Fiber 的工作原理
- 采用双缓存的工作机制
Fiber 通过当前屏幕上显示内容对应的 Fiber 树称为
current Fiber
树和正在内存中构建的 Fiber 树称为workInProgress Fiber
树实现双缓存。通过直接在内存中绘制workInProgress Fiber
树,绘制完毕后直接替换current Fiber
树来实现页面的 dom 的更新。
总结
-
如何做到快速响应
异步可中断更新和 concurrent 模式
-
为什么异步可中断更新可以实现快速响应
当渲染大量 dom 节点这种 CPU 密集型操作时,同步更新会导致掉帧效果,而异步可中断更新不会
-
为什么同步更新在 CPU 密集时会导致掉帧
当同步的 js 计算时间过长产生长任务时,浏览器就没时间去做样式绘制和布局了
-
怎样实现异步可中断更新
Reconciler 协调器 实现了 Fiber ,Fiber 可实现异步可中断的更新
-
为什么要有 React Fiber
Generator 协程可实现中断后恢复,但是不能实现任务优先级调度。所以 React 内部实现的一套状态更新机制。Fiber 支持任务不同优先级,可中断与恢复,并且恢复后可以复用之前的中间状态。
-
Fiber 是如何实现异步可中断更新
不晓得,这可能就是代数效应吧,我们不知道实现,但是知道它可。待源码查询一下 // TODO:
-
为什么 react 15 不能实现异步可中断更新
在 React15 及以前,Reconciler 采用递归的方式创建虚拟 DOM,递归过程是不能中断的。如果组件树的层级很深,递归会占用线程很多时间,造成卡顿。