Vue 和 React 都是现代 MVVM 前端框架,都是以数据驱动页面,让我们在开发时可以专注于数据的处理,基本无需关心数据应该如何渲染在页面上。但两者在处理数据的具体实现上有所不同。
Vue
Vue1
在系统初始化时,Watcher 监听数据的每个属性,当数据发生变化时,框架可以通知我们,我们就可以精确的知道数据的哪个 key 发生了变化,基于此可以去修改真实 DOM。
vue2
因为每个数据都需要一个对应的 Watcher,这样在数据变化时框架才会主动告诉我们修改了哪些数据,但是随着项目的增长,这样的 Watcher 会带来一定的性能损耗,因此基于 Vue1 ,Vue2 中引入了虚拟 DOM 来解决响应式数据过多的问题。
响应式数据是主动推送变化,虚拟 DOM 是被动的计算数据的 Diff。在 Vue2 中,组件之间的变化,可以通过响应式来通知更新,组件内部的数据变化,则通过虚拟 DOM 去更新页面,这样就可以把响应式的监听器控制在组件级别,而虚拟 DOM 控制在了组件的大小。
组件内部,当有数据变化时,会触发组件的重新渲染,重新执行 render 函数,生成新的虚拟 DOM,再比较新旧虚拟 DOM,根据 Diff 结果去需要的 DOM 节点进行修改。
组件间,Vue 的响应式系统会自动追踪跨组件的数据依赖关系,当组件间传递的数据有变化时,比如父子组件间,当父组件的数据变化时,所有依赖该数据的子组件都会被标记为需要更新,并触发重新渲染。
vue3
Vue3 在 Vue2 的基础上做了极大的性能优化,在虚拟 DOM 的静态标记上做到了极致,让静态的部分越过虚拟 DOM 的计算,真正做到了按需更新,很好的提升了性能。
React
React 通过虚拟 DOM 来计算出变化的数据,进而修改真实 DOM。当有数据变化时,会生成一份新的虚拟 DOM,与旧的虚拟 DOM 进行对比,只更改有差异的真实 DOM。
React 中的数据变化之后,我们只能通过新老数据计算 Diff 来得知数据的变化,React 会在以下操作时来进行虚拟 DOM 的 diff:
- 通过 setState 改变数据
- 父组件传递的 props 变化
- 组件首次挂载
- 父组件重新渲染时,会默认对所有子组件触发 Diff
针对虚拟 DOM 过于庞大之后带来的性能问题,React 借鉴了操作系统时间分片的概念,引入了 fiber 架构。即:把整个虚拟 DOM 树微观化,变成链表,然后利用浏览器的空闲时间来计算 Diff。一旦浏览器有需求,我们就可以把没计算完的任务放在一旁,把主进程控制权还给浏览器,等待浏览器下次空闲。
严格来说这个方案并没有减少计算量,但是巧妙的利用了空闲时间做计算,可以解决卡顿的问题。
总结
- Vue:响应式系统 + 虚拟 DOM
- 响应式系统主动通知变化 + 虚拟 DOM 的 diff 计算得出变化,两者相结合,既减少了 Watcher 数量,也减少了虚拟 DOM 树的大小
- Vue3 的静态节点标记极大的提升虚拟 DOM diff 的效率
- React:虚拟 DOM
- 虚拟 DOM 的 diff 计算得出变化
- 当父组件变化后,默认情况下所有子组件也都会重新渲染,引入 fiber 架构来节省 diff 时间,提高 diff 效率