提到 react fiber,大部分人都知道这是一个 react 新特性,看过一些网上的文章,大概能说出“纤程”“一种新的数据结构”“更新时调度机制”等关键词。
但如果被问:
- 有 react fiber,为什么不需要 vue fiber 呢;
- 之前递归遍历虚拟 dom 树被打断就得从头开始,为什么有了 react fiber 就能断点恢复呢;
本文将从两个框架的响应式设计为切入口讲清这两个问题,不涉及晦涩源码,不管有没有使用过 react,阅读都不会有太大阻力。
什么是响应式
无论你常用的是 react,还是 vue,“响应式更新”这个词肯定都不陌生。
响应式,直观来说就是视图会自动更新。如果一开始接触前端就直接上手框架,会觉得这是理所当然的,但在“响应式框架”出世之前,实现这一功能是很麻烦的。
下面我将做一个时间显示器,用原生 js、react、vue 分别实现:
上面是使用旧的 react 时,获得每一帧的时间点,下面是使用 fiber 架构时,获得每一帧的时间点,因为组件渲染被分片,完成一帧更新的时间点反而被推后了,我们把一些时间片去处理用户响应了。
这里要注意,不会出现“一次组件渲染没有完成,页面部分渲染更新”的情况,react 会保证每次更新都是完整的。
但页面的动画确实变得流畅了,这是为什么呢?
我把该项目的代码仓库 down 下来,看了一下它的动画实现:组件动画效果并不是直接修改width
获得的,而是使用的transform:scale
属性搭配 3D 变换。如果你听说过硬件加速,大概知道为什么了:这样设置页面的重新渲染不依赖上图中的渲染主线程,而是在 GPU 中直接完成。也就是说,这个渲染主线程线程只用保证有一些时间片去响应用户交互就可以了。
-<SierpinskiTriangle x={0} y={0} s={1000}> +<SierpinskiTriangle x={0} y={0} s={1000*t}> {this.state.seconds} </SierpinskiTriangle>
修改一下项目代码中 152 行,把图形的变化改为宽度width
修改,会发现即使用 react fiber,动画也会变得相当卡顿,所以这里的流畅主要是 CSS 动画的功劳。(内存不大的电脑谨慎尝试,浏览器会卡死)
react 不如 vue?
我们现在已经知道了 react fiber 是在弥补更新时“无脑”刷新,不够精确带来的缺陷。这是不是能说明 react 性能更差呢?
并不是。孰优孰劣是一个很有争议的话题,在此不做评价。因为 vue 实现精准更新也是有代价的,一方面是需要给每一个组件配置一个“监视器”,管理着视图的依赖收集和数据更新时的发布通知,这对性能同样是有消耗的;另一方面 vue 能实现依赖收集得益于它的模版语法,实现静态编译,这是使用更灵活的 JSX 语法的 react 做不到的。
在 react fiber 出现之前,react 也提供了 PureComponent、shouldComponentUpdate、useMemo,useCallback 等方法给我们,来声明哪些是不需要连带更新子组件。
结语
回到开头的几个问题,答案不难在文中找到:
- react 因为先天的不足——无法精确更新,所以需要 react fiber 把组件渲染工作切片;而 vue 基于数据劫持,更新粒度很小,没有这个压力;
- react fiber 这种数据结构使得节点可以回溯到其父节点,只要保留下中断的节点索引,就可以恢复之前的工作进度;
文章来源:前端私教年年