vlambda博客
学习文章列表

为什么 Vue 3 里没有时间分片?

这是 2019 年发表在 vuejs/rfcs 里的旧文,现在看起来也不过时。于是我把它翻译成了中文。

译文开始:


为什么从 Vue 3 里移除了「时间分片」?

  • 伊撒尔:我看到时间分片已经从 vue-next 代码中移除了,但并没有看到移除原因。有人知道为什么吗?是因为 Vue 3 不需要时间分片还是其他原因呢?

  • Akryum:主要原因是

    1. 复杂度太高了
    2. 收益太低了
    3. Vue 3 太快了没必要再加时间分片了
  • LinusBorg:注意这不意味着以后不会增加时间分片。我们会在未来重新考量投入产出比来决定是否重新实现时间分片。

  • 尤雨溪:在 Web 应用中,「可中断式更新」主要是由大量 CPU 计算加上复杂 DOM 操作引起的。时间分片旨在让应用在 CPU 进行大量计算时也能与用户交互,但时间分片只能对大量 CPU 计算进行优化,无法优化复杂 DOM 操作,因为要确保用户正在操作的界面是最新的状态才行。

    因此,我们可以考虑两种不同的可中断式更新的场景:

    1. CPU 计算量不大,但 DOM 操作非常复杂(比如说你向页面中插入了十万个节点)。这种场景下不管你做不做时间分片,页面都会很卡。
    2. CPU 计算量非常大。理论上时间分片在这种场景里会有较大收益,但是人机交互研究表明,除了动画之外,大部分用户不会觉得 10 毫秒和 100 毫秒有很大区别。

也就是说,时间分片只在 CPU 需要连续计算 100 毫秒以上的情况下才有较大收益。有意思的地方就出现了,在 React 经常会出现 100 毫秒以上的计算量,因为

  1. Fiber 架构的复杂性导致 React 的虚拟 DOM 协调效率较低,这是系统性的问题。
  2. React 使用 JSX 导致它的渲染效率比 template 低,因为 template 很容易做静态分析和优化。
  3. React Hooks 将大部分组件树的优化 API 暴露给开发者,开发者很多时候需要手动调用 useMemo 来优化渲染效率。(译注:这里省略一些例子)这意味着 React 应用默认就有 render 过多的问题。更严重的是,这些优化在 React 里很难自动化,因为
    1. 这些优化要求开发者正确设置依赖数组
    2. 盲目添加 useMemo 会导致应该 render 的没 render。

很不幸,大部分开发者都很懒,不会在每个地方都加上优化,因此大部分 React 应用都会有大量的没必要的 CPU 计算工作。

对比较而言,Vue 解决了上述问题:

  1. Vue 的架构里没有时间分片,也就没有 Fiber,因此简单了很多,这使得渲染可以更快。
  2. Vue 通过分析 template、简化协调过程,做了大量的 AOT 优化,性能测试结果表明大部分的 DOM 内容有 80% 属于静态内容,因此 Vue 3 的协调速度比 Svelte 快,花费的时间比 React 的 1/10 还少。
  3. 通过数据响应式追踪,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 导航有很大的不同。显然后两交互对时间要求更高,所以不能用一个时间来衡量所有交互方式。

  • 尤雨溪:是的没错,但是

    1. 目前文字输入、Tab 导航引发的 UI 更新都能很快完成,甚至都不会超过 16 毫秒。React 团队提供的 demo 是刻意的,实际应用中很少这么做。
    2. 就算文字输入、Tab 导航引发的更新导致了大量的 CPU 计算,这种计算任务也是无法做到事件分片的。比如,一个在线的带编译器的文本编辑框,它需要在用户输入之后马上完成编译,时间分片根本不起作用,还不如使用防抖和节流来优化。

后面还有很多内容,我就不翻译了。

大家面试的时候如果遇到类似的问题,可以用上面的内容来回答。