Open Twlig opened 2 years ago
React 是用 JavaScript 构建快速响应的大型 Web 应用程序的首选方式。
我们日常使用App,浏览网页时,有两类场景会制约快速响应:
快速响应
这两类场景可以概括为:
由于GUI渲染线程与JS线程是互斥的。所以JS脚本执行和浏览器布局、绘制不能同时执行。在每16。6ms内需要完成:
GUI渲染线程
JS线程
JS脚本执行 ----- 样式布局 ----- 样式绘制
因此,在每个刷新周期内,要分配js线程时间和GUI渲染线程时间。在react设计中,给js线程预留的初始时间是5ms(要给GUI线程时间,不然会导致掉帧)。当预留的时间不够用时,React将线程控制权交还给浏览器使其有时间渲染UI,React则等待下一帧时间到来继续被中断的工作,这被称为时间切片。
React
所以,解决CPU瓶颈的关键是实现时间切片,而时间切片的关键是:将同步的更新变为可中断的异步更新。同步更新会导致输入响应速度变慢。
CPU瓶颈
时间切片
React15架构可以分为两层:
主要工作:在触发视图更新时,收集需要更新的虚拟DOM,通知renderer
我们知道,在React中可以通过this.setState、this.forceUpdate、ReactDOM.render等API触发更新。
this.setState
this.forceUpdate
ReactDOM.render
每当有更新发生时,Reconciler会做如下工作:
render
由于React支持跨平台,所以不同平台有不同的Renderer。我们前端最熟悉的是负责在浏览器环境渲染的Renderer —— [ReactDOM](https://www.npmjs.com/package/react-dom)
在Reconciler中,mount的组件会调用mountComponent,update的组件会调用updateComponent 。这两个方法都会递归更新子组件。
mount
update
由于递归执行,所以更新一旦开始,中途就无法中断。当层级很深时,递归更新时间超过了16ms,用户交互就会卡顿。
Reconciler和Renderer是交替工作的,当第一个li在页面上已经变化后,第二个li再进入Reconciler。由于整个过程都是同步的,所以在用户看来所有DOM是同时更新的。
li
但是当中途发生中断:
当第一个li完成更新时中断更新,即步骤3完成后中断更新,此时后面的步骤都还未执行。
用户本来期望123变为246。实际却看见更新不完全的DOM 223!
123
246
223
React16架构可以分为三层:
可以看到,相较于React15,React16中新增了Scheduler(调度器)。
Scheduler为任务设置优先级,决定调度顺序。以浏览器是否有剩余时间作为任务中断的标准,那么需要一种机制,当浏览器有剩余时间时发出通知。
其实部分浏览器已经实现了这个API,这就是requestIdleCallback。但是由于以下因素,React放弃使用:
requestIdleCallback
基于以上原因,React实现了功能更完备的requestIdleCallbackpolyfill,这就是Scheduler。除了在空闲时触发回调的功能外,Scheduler还提供了多种调度优先级供任务设置。
React 16中,Reconciler内部采用了Fiber的架构,从而实现了可中断得异步更新能力。
Reconciler
Fiber
在React 15中Reconciler是递归处理虚拟DOM的。在react 16中,更新工作从递归变成了可以中断的循环过程。每次循环都会调用shouldYield判断当前是否有剩余时间。
shouldYield
/** @noinline */ function workLoopConcurrent() { // Perform work until Scheduler asks us to yield while (workInProgress !== null && !shouldYield()) { workInProgress = performUnitOfWork(workInProgress); } }
那么React16是如何解决中断更新时DOM渲染不完全的问题呢?
在React16中,Reconciler与Renderer不再是交替工作。当Scheduler将任务交给Reconciler后,Reconciler会为变化的虚拟DOM打上代表增/删/更新的标记,类似这样:
export const Placement = /* */ 0b0000000000010; export const Update = /* */ 0b0000000000100; export const PlacementAndUpdate = /* */ 0b0000000000110; export const Deletion = /* */ 0b0000000001000;
整个Scheduler与Reconciler的工作都在内存中进行。只有当所有组件都完成Reconciler的工作,才会统一交给Renderer。
Renderer根据Reconciler为虚拟DOM打的标记,同步执行对应的DOM操作。
原文链接:https://react.iamkasong.com/preparation/idea.html#react%E7%90%86%E5%BF%B5
React理念
React 是用 JavaScript 构建快速响应的大型 Web 应用程序的首选方式。
我们日常使用App,浏览网页时,有两类场景会制约
快速响应
:这两类场景可以概括为:
由于
GUI渲染线程
与JS线程
是互斥的。所以JS脚本执行和浏览器布局、绘制不能同时执行。在每16。6ms内需要完成:因此,在每个刷新周期内,要分配js线程时间和GUI渲染线程时间。在react设计中,给js线程预留的初始时间是5ms(要给GUI线程时间,不然会导致掉帧)。当预留的时间不够用时,
React
将线程控制权交还给浏览器使其有时间渲染UI,React
则等待下一帧时间到来继续被中断的工作,这被称为时间切片。所以,解决
CPU瓶颈
的关键是实现时间切片
,而时间切片
的关键是:将同步的更新变为可中断的异步更新。同步更新会导致输入响应速度变慢。React 15架构
React15架构可以分为两层:
Reconciler(协调器)
主要工作:在触发视图更新时,收集需要更新的虚拟DOM,通知renderer
我们知道,在
React
中可以通过this.setState
、this.forceUpdate
、ReactDOM.render
等API触发更新。每当有更新发生时,Reconciler会做如下工作:
render
方法,将返回的JSX转化为虚拟DOMRenderer(渲染器)
由于
React
支持跨平台,所以不同平台有不同的Renderer。我们前端最熟悉的是负责在浏览器环境渲染的Renderer —— [ReactDOM](https://www.npmjs.com/package/react-dom)React15架构的缺点
在Reconciler中,
mount
的组件会调用mountComponent,update
的组件会调用updateComponent 。这两个方法都会递归更新子组件。由于递归执行,所以更新一旦开始,中途就无法中断。当层级很深时,递归更新时间超过了16ms,用户交互就会卡顿。
Reconciler和Renderer是交替工作的,当第一个
li
在页面上已经变化后,第二个li
再进入Reconciler。由于整个过程都是同步的,所以在用户看来所有DOM是同时更新的。但是当中途发生中断:
当第一个
li
完成更新时中断更新,即步骤3完成后中断更新,此时后面的步骤都还未执行。用户本来期望
123
变为246
。实际却看见更新不完全的DOM223
!React 16架构
React16架构可以分为三层:
可以看到,相较于React15,React16中新增了Scheduler(调度器)。
Scheduler(调度器)
Scheduler为任务设置优先级,决定调度顺序。以浏览器是否有剩余时间作为任务中断的标准,那么需要一种机制,当浏览器有剩余时间时发出通知。
其实部分浏览器已经实现了这个API,这就是requestIdleCallback。但是由于以下因素,
React
放弃使用:requestIdleCallback
触发的频率会变得很低基于以上原因,
React
实现了功能更完备的requestIdleCallback
polyfill,这就是Scheduler。除了在空闲时触发回调的功能外,Scheduler还提供了多种调度优先级供任务设置。Reconciler(协调器)
React 16中,
Reconciler
内部采用了Fiber
的架构,从而实现了可中断得异步更新能力。在React 15中Reconciler是递归处理虚拟DOM的。在react 16中,更新工作从递归变成了可以中断的循环过程。每次循环都会调用
shouldYield
判断当前是否有剩余时间。那么React16是如何解决中断更新时DOM渲染不完全的问题呢?
在React16中,Reconciler与Renderer不再是交替工作。当Scheduler将任务交给Reconciler后,Reconciler会为变化的虚拟DOM打上代表增/删/更新的标记,类似这样:
整个Scheduler与Reconciler的工作都在内存中进行。只有当所有组件都完成Reconciler的工作,才会统一交给Renderer。
Renderer(渲染器)
Renderer根据Reconciler为虚拟DOM打的标记,同步执行对应的DOM操作。
React 15和React 16的区别
原文链接:https://react.iamkasong.com/preparation/idea.html#react%E7%90%86%E5%BF%B5