2025-05-03 日报 Day175

2025-05-03 日报 Day175

Yuyang 前端小白🥬

今日的鸡汤

每次归程,都是为了更好出发;每次停歇,都是为了积攒力量。

今日学习内容

1、https://react.iamkasong.com/

今日笔记

1、React理念:我们日常使用 App,浏览网页时,有两类场景会制约快速响应:

  • 当遇到大计算量的操作或者设备性能不足使页面掉帧,导致卡顿。
  • 发送网络请求后,由于需要等待数据返回才能进一步操作导致不能快速响应。
    这两类场景可以概括为:
  • CPU 的瓶颈
  • IO 的瓶颈

CPU瓶颈: 当项目变得庞大、组件数量繁多时,就容易遇到 CPU 的瓶颈。
主流浏览器刷新频率为 60Hz,即每(1000ms / 60Hz)16.6ms 浏览器刷新一次。
我们知道,JS 可以操作 DOM,GUI渲染线程与JS线程是互斥的。所以JS 脚本执行和浏览器布局、绘制不能同时执行。
在每 16.6ms 时间内,需要完成如下工作:
JS脚本执行 —– 样式布局 —– 样式绘制
当 JS 执行时间过长,超出了 16.6ms,这次刷新就没有时间执行样式布局和样式绘制了。

如何解决这个问题呢?
在浏览器每一帧的时间中,预留一些时间给 JS 线程,React利用这部分时间更新组件(可以看到,在源码中,预留的初始时间是 5ms)。
当预留的时间不够用时,React将线程控制权交还给浏览器使其有时间渲染 UI,React则等待下一帧时间到来继续被中断的工作。
这种将长任务分拆到每一帧中,像蚂蚁搬家一样一次执行一小段任务的操作,被称为时间切片(time slice)
解决CPU瓶颈的关键是实现时间切片,而时间切片的关键是:将同步的更新变为可中断的异步更新。

IO瓶颈: 当我们发送网络请求后,等待数据返回才能进一步操作时,就会遇到 IO 的瓶颈。
如何在网络延迟客观存在的情况下,减少用户对网络延迟的感知?
将人机交互研究的结果整合到真实的 UI 中。

通过以上内容,我们可以看到,React为了践行“构建快速响应的大型 Web 应用程序”理念做出的努力。

其中的关键是解决 CPU 的瓶颈与 IO 的瓶颈。而落实到实现上,则需要将同步的更新变为可中断的异步更新。

2、React15架构:
React15 架构可以分为两层:

  • Reconciler(协调器)—— 负责找出变化的组件
  • Renderer(渲染器)—— 负责将变化的组件渲染到页面上

Reconciler:
每当有更新发生时,Reconciler会做如下工作:
调用函数组件、或 class 组件的render方法,将返回的 JSX 转化为虚拟 DOM
将虚拟 DOM 和上次更新时的虚拟 DOM 对比
通过对比找出本次更新中变化的虚拟 DOM
通知Renderer将变化的虚拟 DOM 渲染到页面上

Renderer:
由于React支持跨平台,所以不同平台有不同的Renderer。我们前端最熟悉的是负责在浏览器环境渲染的Renderer —— ReactDOM。

除此之外,还有:

ReactNative渲染器,渲染 App 原生组件
ReactTest渲染器,渲染出纯 Js 对象用于测试
ReactArt渲染器,渲染到 Canvas, SVG 或 VML (IE8)
在每次更新发生时,Renderer接到Reconciler通知,将变化的组件渲染在当前宿主环境。

递归更新的缺点
由于递归执行,所以更新一旦开始,中途就无法中断。当层级很深时,递归更新时间超过了 16ms,用户交互就会卡顿。

在上一节中,我们已经提出了解决办法——用可中断的异步更新代替同步的更新。那么 React15 的架构支持异步更新么?让我们看一个例子:

image-20250630214550905

3、React16架构:
React16 架构可以分为三层:

Scheduler(调度器)—— 调度任务的优先级,高优任务优先进入Reconciler
Reconciler(协调器)—— 负责找出变化的组件
Renderer(渲染器)—— 负责将变化的组件渲染到页面上
可以看到,相较于 React15,React16 中新增了Scheduler(调度器),让我们来了解下他。

Scheduler(调度器):既然我们以浏览器是否有剩余时间作为任务中断的标准,那么我们需要一种机制,当浏览器有剩余时间时通知我们。其实部分浏览器已经实现了这个 API,这就是requestIdleCallback。但是由于以下因素,React放弃使用:
浏览器兼容性
触发频率不稳定,受很多因素影响。比如当我们的浏览器切换 tab 后,之前 tab 注册的requestIdleCallback触发的频率会变得很低
基于以上原因,React实现了功能更完备的requestIdleCallbackpolyfill,这就是Scheduler。除了在空闲时触发回调的功能外,Scheduler还提供了多种调度优先级供任务设置。
Reconciler(协调器):在 React15 中Reconciler是递归处理虚拟 DOM 的。React16中的Reconciler更新工作从递归变成了可以中断的循环过程。每次循环都会调用shouldYield判断当前是否有剩余时间。

1
2
3
4
5
6
7
/** @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 打上代表增/删/更新的标记,类似这样:

1
2
3
4
export const Placement = /*             */ 0b0000000000010;
export const Update = /* */ 0b0000000000100;
export const PlacementAndUpdate = /* */ 0b0000000000110;
export const Deletion = /* */ 0b0000000001000;

整个Scheduler与Reconciler的工作都在内存中进行。只有当所有组件都完成Reconciler的工作,才会统一交给Renderer。

Renderer (渲染器):Renderer根据Reconciler为虚拟 DOM 打的标记,同步执行对应的 DOM 操作。

此页目录
2025-05-03 日报 Day175