React设计理念
Opened this issue · 0 comments
全文约 1500 字,阅读完大约需要 6 分钟,读完本文,你可以 get 到,
- React 的设计理念是什么?
- 有哪些因素可以制约网页的快速响应?
- 什么是 CPU 的瓶颈?
- 什么是 IO 瓶颈?
- 什么是掉帧?
- React 如何将人机交互研究的结果整合到真实的 UI 中?
- React 引入新 Fiber 架构的原因?
正式开始之前,建议先阅读官网的 React 哲学 一文 ,
假设你已经阅读完 React 哲学,文中提到,
React 是用 JavaScript 构建快速响应的大型 Web 应用程序的首选方式,它在 Facebook 和 Instagram 上表现优秀。
可见 快速响应
是 React 设计中至关重要的一个理念,那么如何实现 快速响应
呢?
这里我们可以从相反的角度考虑,制约快速响应的因素有哪些?
一般来说,网页的响应速度受以下两类场景的约束,
- 遇到大计算量的操作或者设备性能不足导致的页面掉帧,卡顿
- 等待网络请求数据返回才能进一步操作导致的 IO 耗时
这两类场景可以概括为,
- CPU的瓶颈
- IO 的瓶颈
React 是如何解决这两个瓶颈的呢?
CPU的瓶颈
要解决 CPU 瓶颈,首先要明白什么是 “掉帧” 。
我们知道主流浏览器的刷新频率为 60Hz(1000ms / 60Hz),即每 16.6ms 刷新一次,而 Javascript 是可以操作 DOM 的,为了避免无法预期的渲染结果,在浏览器的架构设计上,渲染进程和 JS 引擎是互斥的,所以当一帧内 Javascript 脚本执行的时间过长(超过了浏览器的刷新时间 16.6ms)时,本次刷新就没有时间去执行样式布局和样式绘制了,也就导致了所谓的 “掉帧”。
比如下面的堆栈图打印,我们可以看到 Javascript 的执行时间为 73.65ms,远多于一帧的时间,
明白了原理,解决这个问题就很简单了,我们需要在浏览器每一帧的时间中,预留一部分时间给 JS 线程,然后把控制权交还给渲染进程,让浏览器有剩余时间去执行样式布局和绘制。
在源码里,预留的时间是 5ms,
// Scheduler periodically yields in case there is other work on the main
// thread, like user events. By default, it yields multiple times per frame.
// It does not attempt to align with frame boundaries, since most tasks don't
// need to be frame aligned; for those that do, use requestAnimationFrame.
let yieldInterval = 5;
当预留时间不够时,React 将控制权交还给渲染进程使其有时间来渲染 UI,React 则等待下一帧时间到来继续被中断的工作。
这种将长任务分拆到每一帧中,像蚂蚁搬家一样一次执行一小段任务的操作,叫做时间切片(Time Slice),这与进程和网络调度程序常用的 时间片轮转调度 (Round-Robin Scheduling) 算法类似,这种方式的缺点是任务运行的总时间变长了,但可以明显减少掉帧的可能性,对于浏览器渲染而言,这种取舍是有必要的,
所以解决 CPU 瓶颈的关键是 时间切片
,而时间切片的关键是:将同步更新变为可中断的异步更新。
IO的瓶颈
前面我们介绍到,IO 的瓶颈主要来源于 网络延迟
,但实际上,网络延迟
是前端开发者无法解决的。
如何在网络延迟客观存在的前提下,减少用户对延迟的感知呢?
我们可以在目前人机交互最顶尖的苹果(IOS)系统中找到一些灵感,以设置面板中的“通用”和“Siri与搜索”为例,
这里的点击“通用”的交互是同步的,而点击“Siri与搜索”是异步的,但从用户感知上看,两者的区别微乎其微,怎么做到的?
仔细观察我们发现,点击“Siri与搜索”后,系统在当前页面停留了一小段时间,这一小段时间被用来请求数据,当“这一小段时间”足够短时,用户是无感知的,如果请求时间超过一个范围,再显示 loading 的效果。这其实是人机交互研究的一个结果,通过类似的方案,可以明显改善用户对网络延迟的感知。
React 借鉴并应用了这一研究成果,实现了 Suspense 及配套的 useDeferredValue,将人机交互研究的结果整合到真实的 UI 中。
而在源码内部,为了支持这些特性,同样需要将同步的更新变为可中断的异步更新。
设计理念
综上,我们不难发现 React 为了践行 “构建快速响应的大型 Web 应用程序” 做出的努力,其设计理念可以简单概括为快速响应,
快速响应的关键在于解决 CPU 和 IO 瓶颈,
具体实现上,需要将同步的更新变为可中断的异步更新,这也是为什么 React 需要引入新的 Fiber 架构的原因。
写在最后
本文首发于我的 博客,才疏学浅,难免有错误,文章有误之处还望不吝指正!
如果有疑问或者发现错误,可以在评论区进行提问和勘误,
如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。