You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
/** @noinline */functionworkLoopConcurrent(){// Perform work until Scheduler asks us to yieldwhile(workInProgress!==null&&!shouldYield()){workInProgress=performUnitOfWork(workInProgress);}}
React 的理念
React 的理念
快速响应即:速度快,响应自然。
响应快
由于语法的灵活,在编译时无法区分可能变化的部分。所以在运行时,
React
需要遍历每个元素,判断其数据是否更新。基于以上原因,相比于Vue
、Angular
,缺少编译时优化手段的React
为了速度快需要在运行时做出更多努力。PureComponent
或React.memo
构建组件shouldComponentUpdate
生命周期钩子key
useCallback
和useMemo
缓存函数和变量由开发者来显式的告诉
React
哪些组件不需要重复计算、可以复用。响应自然
将同步的更新变为可中断的异步更新。
React15架构
React 15的架构可以分为两层:
Reconciler(协调器)
在
React
中可以通过this.setState
、this.forceUpdate
、ReactDOM.render
等API触发更新。每当有更新发生时,Reconciler会做如下工作:
render
方法,将返回的JSX转化为虚拟DOMRenderer(渲染器)
由于
React
支持跨平台,所以不同平台有不同的Renderer。我们前端最熟悉的是负责在浏览器环境渲染的Renderer —— ReactDOM。除此之外,还有:
在每次更新发生时,Renderer接到Reconciler通知,将变化的组件渲染在当前宿主环境。
React15架构的缺点
mount
的组件会调用mountComponent,update
的组件会调用updateComponent。这两个方法都会递归更新子组件。GUI渲染线程
与JS线程
是互斥的。所以JS脚本执行和浏览器布局、绘制不能同时执行。】Reconciler
采用递归的方式创建虚拟DOM,递归过程是不能中断的。如果组件树的层级很深,递归会占用线程很多时间,造成卡顿。React 16架构
React16架构可以分为三层:
Scheduler(调度器)
既然我们以浏览器是否有剩余时间作为任务中断的标准,那么我们需要一种机制,当浏览器有剩余时间时通知我们。
React
放弃使用 requestIdleCallback原因:【浏览器对 requestIdleCallback 和 requestAnimationFrame 实现了类似功能】requestIdleCallback
触发的频率会变得很低基于以上原因,
React
实现了功能更完备的requestIdleCallback
polyfill,这就是Scheduler。除了在空闲时触发回调的功能外,Scheduler还提供了多种调度优先级供任务设置。Reconciler(协调器)
从
React15
到React16
,协调器(Reconciler
)重构的一大目的是:将老的同步更新
的架构变为异步可中断更新
。异步可中断更新
可以理解为:更新
在执行过程中可能会被打断(①有其他更高优先级任务需要先更新②当前帧没有剩余时间),当可以继续执行时恢复之前执行的中间状态。那么React16是如何解决中断更新时DOM渲染不完全的问题呢?
在React16中,Reconciler与Renderer不再是交替工作【React15架构的Reconciler和Renderer是交替工作的】。当Scheduler将任务交给Reconciler后,Reconciler会为变化的虚拟DOM打上代表增/删/更新的标记,类似这样:
整个Scheduler与Reconciler的工作都在内存中进行,不会更新到DOM上面。【所以即使反复中断,用户也不会看见更新不完全的DOM】只有当所有组件都完成Reconciler的工作,才会统一交给Renderer。
Reconciler 内部采用了 Fiber 的结构。
Renderer(渲染器)
Renderer根据Reconciler为虚拟DOM打的标记,同步执行对应的DOM操作。
Fiber
Fiber 的含义
Fiber
包含三层含义:作为架构来说,之前
React15
的Reconciler
采用递归的方式执行,数据保存在递归调用栈中,所以被称为stack Reconciler
。React16
的Reconciler
基于Fiber节点
实现,被称为Fiber Reconciler
。每个Fiber节点有个对应的
React element
,多个Fiber节点
通过如下三个属性连接成树。作为静态的数据结构来说,每个
Fiber节点
对应一个React element
,保存了该组件的类型(函数组件/类组件/原生组件...)、对应的DOM节点等信息。作为动态的工作单元来说,每个
Fiber节点
保存了本次更新中该组件改变的状态、要执行的工作(需要被删除/被插入页面中/被更新...)另外,如下两个字段保存调度优先级相关的信息,会在讲解
Scheduler
时介绍。Fiber 工作原理
Fiber节点
可以保存对应的DOM节点
。相应的,Fiber节点
构成的Fiber树
就对应DOM树
。那么如何更新DOM
呢?这需要用到被称为“双缓存”的技术。双缓存是什么
canvas
绘制动画,每一帧绘制前都会调用ctx.clearRect
清除上一帧的画面。React
使用“双缓存”来完成Fiber树
的构建与替换——对应着DOM树
的创建与更新。双缓存Fiber树
在
React
中最多会同时存在两棵Fiber树
。当前屏幕上显示内容对应的Fiber树
称为current Fiber树
,正在内存中构建的Fiber树
称为workInProgress Fiber树
。current Fiber树
中的Fiber节点
被称为current fiber
,workInProgress Fiber树
中的Fiber节点
被称为workInProgress fiber
,他们通过alternate
属性连接。React
应用的根节点通过current
指针在不同Fiber树
的rootFiber
间切换来实现Fiber树
的切换。当
workInProgress Fiber树
构建完成交给Renderer
渲染在页面上后,应用根节点的current
指针指向workInProgress Fiber树
,此时workInProgress Fiber树
就变为current Fiber树
。每次状态更新都会产生新的
workInProgress Fiber树
,通过current
与workInProgress
的替换,完成DOM
更新。Fiber树
的构建与替换过程Fiber树
的构建与替换过程,这个过程伴随着DOM
的更新。以具体例子讲解
mount时
、update时
的构建/替换流程mount 时
首次执行
ReactDOM.render
会创建fiberRootNode
(源码中叫fiberRoot
)和rootFiber
。其中**fiberRootNode
是整个应用的根节点,rootFiber
是<App/>
所在组件树的根节点**。之所以要区分
fiberRootNode
与rootFiber
,是因为在应用中我们可以多次调用ReactDOM.render
渲染不同的组件树,他们会拥有不同的rootFiber
。但是整个应用的根节点只有一个,那就是fiberRootNode
。fiberRootNode
的current
会指向当前页面上已渲染内容对应对Fiber树
,被称为current Fiber树
。由于是首屏渲染,页面中还没有挂载任何
DOM
,所以fiberRootNode.current
指向的rootFiber
没有任何子Fiber节点
(即current Fiber树
为空)。
接下来进入
render阶段
,根据组件返回的JSX
在内存中依次创建Fiber节点
并连接在一起构建Fiber树
,被称为workInProgress Fiber树
。(下图中右侧为内存中构建的树,左侧为页面显示的树)在构建
workInProgress Fiber树
时会尝试复用current Fiber树
中已有的Fiber节点
内的属性,在首屏渲染
时只有rootFiber
存在对应的current fiber
(即rootFiber.alternate
)。图中右侧已构建完的
workInProgress Fiber树
在commit阶段
渲染到页面。此时
DOM
更新为右侧树对应的样子。fiberRootNode
的current
指针指向workInProgress Fiber树
使其变为current Fiber 树
。update 时
1.接下来我们点击
p节点
触发状态改变,这会开启一次新的render阶段
并构建一棵新的workInProgress Fiber 树
。和
mount
时一样,workInProgress fiber
的创建可以复用current Fiber树
对应的节点数据。2.
workInProgress Fiber 树
在render阶段
完成构建后进入commit阶段
渲染到页面上。渲染完毕后,workInProgress Fiber 树
变为current Fiber 树
。Fiber Reconciler 与 Stack Reconciler 的不同
Fiber 是一种轻量的执行线程,同线程一样共享定址空间,线程靠系统调度,并且是抢占式多任务处理,Fiber 则是自调用,协作式多任务处理。
首先,使用协作式多任务处理任务。将原来的整个 Virtual DOM 的更新任务拆分成一个个小的任务。每次做完一个小任务之后,放弃一下自己的执行将主线程空闲出来,看看有没有其他的任务。如果有的话,就暂停本次任务,执行其他的任务,如果没有的话,就继续下一个任务。
整个页面更新并重渲染过程分为两个阶段。
所以之前浏览器主线程执行更新任务的执行流程就变成了这样。
其次,对任务进行优先级划分。不是每来一个新任务,就要放弃现执行任务,转而执行新任务。与我们做事情一样,将任务划分优先级,只有当比现任务优先级高的任务来了,才需要放弃现任务的执行。比如说,屏幕外元素的渲染和更新任务的优先级应该小于响应用户输入任务。若现在进行屏幕外组件状态更新,用户又在输入,浏览器就应该先执行响应用户输入任务。浏览器主线程任务执行流程如下图所示。
使用了 ReactFiber 去渲染整个页面,ReactFiber 会将整个更新任务分成若干个小的更新任务,然后设置一些任务默认的优先级。每执行完一个小任务之后,会释放主线程。
需要考虑的问题:
React Fiber 也是带来了很多的好处的。
资料引用
The text was updated successfully, but these errors were encountered: