为什么 Vue 3 里没有时间分片?
这是 2019 年发表在 vuejs/rfcs 里的旧文,现在看起来也不过时。于是我把它翻译成了中文。
译文开始:
为什么从 Vue 3 里移除了「时间分片」?
-
伊撒尔:我看到时间分片已经从 vue-next 代码中移除了,但并没有看到移除原因。有人知道为什么吗?是因为 Vue 3 不需要时间分片还是其他原因呢?
-
Akryum:主要原因是
-
复杂度太高了 -
收益太低了 -
Vue 3 太快了没必要再加时间分片了 -
LinusBorg:注意这不意味着以后不会增加时间分片。我们会在未来重新考量投入产出比来决定是否重新实现时间分片。
-
尤雨溪:在 Web 应用中,「可中断式更新」主要是由大量 CPU 计算加上复杂 DOM 操作引起的。时间分片旨在让应用在 CPU 进行大量计算时也能与用户交互,但时间分片只能对大量 CPU 计算进行优化,无法优化复杂 DOM 操作,因为要确保用户正在操作的界面是最新的状态才行。
因此,我们可以考虑两种不同的可中断式更新的场景:
-
CPU 计算量不大,但 DOM 操作非常复杂(比如说你向页面中插入了十万个节点)。这种场景下不管你做不做时间分片,页面都会很卡。 -
CPU 计算量非常大。理论上时间分片在这种场景里会有较大收益,但是人机交互研究表明,除了动画之外,大部分用户不会觉得 10 毫秒和 100 毫秒有很大区别。
也就是说,时间分片只在 CPU 需要连续计算 100 毫秒以上的情况下才有较大收益。有意思的地方就出现了,在 React 经常会出现 100 毫秒以上的计算量,因为
-
Fiber 架构的复杂性导致 React 的虚拟 DOM 协调效率较低,这是系统性的问题。 -
React 使用 JSX 导致它的渲染效率比 template 低,因为 template 很容易做静态分析和优化。 -
React Hooks 将大部分组件树的优化 API 暴露给开发者,开发者很多时候需要手动调用 useMemo 来优化渲染效率。(译注:这里省略一些例子)这意味着 React 应用默认就有 render 过多的问题。更严重的是,这些优化在 React 里很难自动化,因为 -
这些优化要求开发者正确设置依赖数组 -
盲目添加 useMemo 会导致应该 render 的没 render。
很不幸,大部分开发者都很懒,不会在每个地方都加上优化,因此大部分 React 应用都会有大量的没必要的 CPU 计算工作。
对比较而言,Vue 解决了上述问题:
-
Vue 的架构里没有时间分片,也就没有 Fiber,因此简单了很多,这使得渲染可以更快。 -
Vue 通过分析 template、简化协调过程,做了大量的 AOT 优化,性能测试结果表明大部分的 DOM 内容有 80% 属于静态内容,因此 Vue 3 的协调速度比 Svelte 快,花费的时间比 React 的 1/10 还少。 -
通过数据响应式追踪,Vue 可以做到组件树级别的优化,比如把插槽编译为函数以避免 children 的变化引发 re-render,比如自动缓存内联事件处理函数以避免 re-render。Vue 3 可以做到在不借助开发者的任何手动优化的情况下,防止子组件在非必要的情况下 re-render。这意味着同样一次更新,React 应用可能要 re-render 多个组件,而 Vue 应用很可能只 re-render 一个组件。
因此,在默认情况下,Vue 3 应用会比 React 应用少花费很多 CPU 时间,因而遇到 CPU 连续计算时间超过 100 毫秒的机会相当少,除非是极端情况。但大部分极端情况是 DOM 操作过于复杂,而不是 CPU 计算量太大。
另外,时间分片,或者说并发模式,给 React 带来了另外一个问题:React 需要对所有更新任务进行调度和调和,这导致 React 还需要搞定任务优先级、任务失效处理、re-entry 等任务,这会使 React 变得更复杂,进而让源码的体积膨胀。就算 React 把 Suspense、Tree-shaking 等优化都加上,Vue 3 的运行时体积也只有 React + ReactDOM 的 1/4。
注意我并不是说并发模式是个馊主意,并发模式确实对处理某些问题提供了有意思的新途径(尤其是在协调异步状态转换时 coordinating async state transitions),但是为此而实现时间分片是否值得,还需要再三权衡,至少现在不值得 Vue 3 这样做。
340人点赞,8人点踩。
-
伊撒尔:谢谢 @尤雨溪 的回复,你总结得很不错。
确实时间分片解决的问题并不多,只解决了很少一部分场景的问题,比如动画和可视化。99% 的场景不需要时间分片,时间分片只会延长整个渲染时长。
React 有很多问题,我这里补充一点,Fiber 的链表遍历制约了 React 的 diff 算法、让很多优化变得无法实施。
总得来说,Vue 3 对利弊的权衡对我很有说服力。
-
CyberAP:我认为关于 100 毫秒的论述有些问题,因为浏览器上有很多种不同的人机交互方式,按钮点击跟文字输入、Tab 导航有很大的不同。显然后两种交互对时间要求更高,所以不能用一个时间来衡量所有交互方式。
-
尤雨溪:是的没错,但是
-
目前文字输入、Tab 导航引发的 UI 更新都能很快完成,甚至都不会超过 16 毫秒。React 团队提供的 demo 是刻意的,实际应用中很少这么做。 -
就算文字输入、Tab 导航引发的更新导致了大量的 CPU 计算,这种计算任务也是无法做到事件分片的。比如,一个在线的带编译器的文本编辑框,它需要在用户输入之后马上完成编译,时间分片根本不起作用,还不如使用防抖和节流来优化。
后面还有很多内容,我就不翻译了。
大家面试的时候如果遇到类似的问题,可以用上面的内容来回答。