我们可以从官网(opens new window)看到React的理念:

我们认为,React 是用 JavaScript 构建快速响应的大型 Web 应用程序的首选方式。它在 Facebook 和 Instagram 上表现优秀。

CPU的瓶颈

当项目变得庞大、组件数量繁多时,就容易遇到CPU的瓶颈。
考虑如下Demo,我们向视图中渲染3000个li:

  1. function App() {
  2. const len = 3000;
  3. return (
  4. <ul>
  5. {Array(len).fill(0).map((_, i) => <li>{i}</li>)}
  6. </ul>
  7. );
  8. }
  9. const rootEl = document.querySelector("#root");
  10. ReactDOM.render(<App/>, rootEl);

主流浏览器刷新频率为60Hz,即每(1000ms / 60Hz)16.6ms浏览器刷新一次
我们知道,JS可以操作DOM,GUI渲染线程与JS线程是互斥的。所以JS脚本执行浏览器布局、绘制不能同时执行。
在每16.6ms时间内,需要完成如下工作:
JS脚本执行 ——- 样式布局 ——- 样式绘制
当JS执行时间过长,超出了16.6ms,这次刷新就没有时间执行样式布局样式绘制了。
在Demo中,由于组件数量繁多(3000个),JS脚本执行时间过长,页面掉帧,造成卡顿。
可以从打印的执行堆栈图看到,JS执行时间为73.65ms,远远多于一帧的时间。
image.png
如何解决这个问题呢?
答案是:在浏览器每一帧的时间中,预留一些时间给JS线程,React利用这部分时间更新组件(可以看到,在源码(opens new window)中,预留的初始时间是5ms)
接下来我们开启Concurrent Mode(后续章节会讲到,当前你只需了解开启后会启用时间切片):
此时我们的长任务被拆分到每一帧不同的task中,JS脚本执行时间大体在5ms左右,这样浏览器就有剩余时间执行样式布局样式绘制,减少掉帧的可能性。
image.png
所以,解决CPU瓶颈的关键是实现时间切片,而时间切片的关键是:将同步的更新变为可中断的异步更新

网络瓶颈

先在当前页面停留了一小段时间,这一小段时间被用来请求数据。
当“这一小段时间”足够短时,用户是无感知的。如果请求时间超过一个范围,再显示loading的效果。
为此,React实现了Suspense(opens new window)功能及配套的hook——useDeferredValue(opens new window)
而在源码内部,为了支持这些特性,同样需要将同步的更新变为可中断的异步更新

代数效应

代数效应指的是将函数中的副作用,从函数中剥离出去。而react正是一直践行着代数效应的思想。

举例

async 函数具有传染性,所以有没有一种语法能够不改变影响函数的情况下,能够支持同步和异步函数执行呢 ?
这里伪造一个语法,perform 和 resume。类似于 try catch
image.png
image.png