理念篇
- 快速响应的瓶颈之一就是cpu限制,浏览器的js线程与gui渲染线程互斥,一旦js执行过长,就会导致卡顿。
解决这个问题的关键就是:在浏览器每一帧的时间中,预留一些时间给JS线程,React利用这部分时间更新组件
解决CPU瓶颈的关键是实现时间切片,而时间切片的关键是:将同步的更新变为可中断的异步更新。
- 瓶颈之二就是IO的瓶颈:
React
实现了[Suspense ]功能及配套的hook
——useDeferredValue为了支持这些特性,同样需要将同步的更新变为可中断的异步更新。
老的react架构
分为两层:Reconciler(协调器)—— 负责找出变化的组件. Renderer(渲染器)—— 负责将变化的组件渲染到页面上
当有更新变化的时候,Reconclier会调用函数组件或者类组件的render方法,得到虚拟dom,然后进行diff算法比对,将变化的虚拟dom叫个Renderer渲染到页面上。
该架构的缺点:在Reconciler中,mount组件调用mountComponent方法,而update组件调用updateComponent方法,两个方法都会递归更新子组件,一旦开始,无法中断,当层级很深的时候,就会超过16ms,导致卡顿。react15的架构支持中断异步更新吗?
答案是否定的:老的架构,Reconciler和Renderer阶段是交替工作的,当有一处变化的时候,Reconciler比对完变化就立马给到Renderer去渲染,然后再继续寻找变化,如果中途断掉的话,这样就会导致用户可能会看到渲染不完全的ui。
新的react架构
react16的架构分为三层:
- Scheduler(调度器)—— 调度任务的优先级,高优任务优先进入Reconciler
- Reconciler(协调器)—— 负责找出变化的组件
- Renderer(渲染器)—— 负责将变化的组件渲染到页面上
scheduler
react16新增了一个调度器scheduler,因为需要以浏览器是否有剩余时间作为任务中断的标准,需要一种机制,当浏览器有剩余时间的时候通知我们。比如部分浏览器实现的requeistIdleCallback,但是因为浏览器兼容性和触发频率的不稳定性,react实现了功能更加完备的requestIdleCallback
polyfill,就是S cheduler,除了能在浏览器空闲时出发回调,还可以提供多种任务调度的功能。
Reconciler
react15中·,Reconciler是递归处理虚拟do m的,而react16的Reconciler,从递归变成了可以中断的循环过程,每次循环都会调用shouldYeild判断当前是否有剩余时间,再决定是否继续处理j s。
/** @noinline */
function workLoopConcurrent() {
// Perform work until Scheduler asks us to yield
while (workInProgress !== null && !shouldYield()) {
workInProgress = performUnitOfWork(workInProgress);
}
}
并且为了解决中断更新渲染不完全的问题,Reconciler和Renderer不再交替工作,而是当Schedule将任务交给Reconciler后,Reconciler处理所有fiber,将变化的fiber打上effectTag标记,只有所有的组件都完成了Reconciler工作,才会统一交给Renderer。
Schedule和Reconciler的工作是在内存中完成的,是可以中断(当前帧没有剩余时间,或者有其他更高优先级任务需要更新)的,用户并没有体验,而Renderer阶段是不可以中断的,如果中断会导致页面渲染不完整。
简而言之,15的Reconciler和Renderer是交替进行的,中断会导致不完全的ui,而react16在所有的组件完成Reconciler阶段,才统一交给R enderer阶段去渲染,所以这中间,Reconciler是可以因为某些原因中断的。
FIber
虚拟Dom无法满足可中断的异步更新需求。Fiber应运而生,作为架构,Recocniler基于Fiber节点实现,称为Fiber Reconciler,而作为静态的数据结构,fiber对应每一个React element,保存了组件的类型,对应的dom信息,相当于对应每一个虚拟dom。作为动态的工作单元,每个fiber节点保存了本次更新中组件的状态。
学习文章地址:https://react.iamkasong.com/