React理念

# React 理念
从官网可看到React理念: React是用js构建快速响应的大型Web应用程序的首选方式。 那么,制约快速响应的因素是什么:
- CPU瓶颈:当遇到大计算量的操作或设备性能不足使页面掉帧,导致卡顿
- IO瓶颈:发送网络请求后,需等待数据返回才能进一步操作导致不能快速响应 React 是如何解决这两个瓶颈呢?
# CPU瓶颈
当项目变庞大、组件数量多,就容易遇到CPU瓶颈 如下Demo,向视图中渲染3000个li
function App() {
const len = 3000;
return (
<ul>
{Array(len).fill(0).map((_, i) => <li>{i}</li>)}
</ul>
);
}
const rootEl = document.querySelector("#root");
ReactDOM.render(<App/>, rootEl);
2
3
4
5
6
7
8
9
10
11
主流浏览器刷新频率:60Hz(1000ms/60Hz),即16.6ms浏览器刷新一次。 我们知道,JS可操作DOM,GUI渲染线程JS线程是互斥的。所以JS脚本执行和浏览器布局、绘制不能同时执行。 在每16.6ms内,需完成如下工作:
JS脚本执行 --- 样式布局 --- 样式绘制
当JS执行时间 过长,超出16.6ms,这次刷新就无时间执行样式布局和绘制
在Demo中,由于组件数量多(3000个),JS脚本执行时间过长,页面掉帧,造成卡顿。
从打印的执行堆栈图看到,JS执行时间73.65ms,远多于一帧时间
如何解决这个问题?
答案是:在浏览器每一帧中,预留一些时间给JS线程,React用这些时间更新组件;若预留的时间不够,React将线程控制权交还给浏览器使其有时间渲染UI,react则等待下一帧时间来继续被中断的工作。
在源码 (opens new window)中,预留的时间是5ms。
这种将长任务分拆到每一帧中,称为时间切片(time slice)
如何开启时间?
开启Concurrent Mode可开启时间切片。
// 通过使用ReactDOM.unstable_createRoot开启Concurrent Mode
// ReactDOM.render(<App/>, rootEl);
ReactDOM.unstable_createRoot(rootEl).render(<App/>);
2
3
此时可看到,长任务被分拆到每一帧不同的task中,JS脚本执行时间大体在5ms左右,这样浏览器就有时间执行样式布局和绘制,减少掉帧的可能性。
所以,解决CPU瓶颈的关键是时间切片,而时间切片的关键是将同步的更新变为可中断的异步更新
# IO瓶颈
网络延迟是前端开发者无法解决的。那么如何在网络延迟下,减少用户对网络延迟的感知呢?
React的答案是将人机交互研究的结果整合到真实的UI中。
这里以IOS系统为例子
从设置页面,进入通用页面(不涉及网络请求)
作为对比,从设置进入Siri与搜索页面
两者的区别?
前者的交互是同步的,直接显示后续页面
后者的交互是异步的,需等待请求返回再显示后续页面。
但从用户感知看,两者区别微乎其微。
这里的做法是:在点击“Siri与搜索”后,先在当前页停留一小段时间,用来请求数据;若这段时间足够短,用户则无感知,若请求时间超过范围,再显示loading效果。
为此,React实现了Suspense (opens new window)功能及配套hook——useDeferredValue (opens new window)
在源码内部,为支持这些特性,需将同步的更新变为可中断的异步更新
# 总结
React为践行构建快速响应的大型Web应用程序,主要解决了CPU的瓶颈和IO瓶颈两大关键问题。主要是将同步更新变为可中断的异步更新