Vue3.0性能提升主要是通过哪几方面体现的?
面试官:Vue3.0性能提升主要是通过哪几方面体现的?
一、编译阶段
回顾Vue2
,我们知道每个组件实例都对应一个 watcher
实例,它会在组件渲染的过程中把用到的数据property
记录为依赖,当依赖发生改变,触发setter
,则会通知watcher
,从而使关联的组件重新渲染
试想一下,一个组件结构如下图
1 | <template> |
可以看到,组件内部只有一个动态节点,剩余一堆都是静态节点,所以这里很多 diff
和遍历其实都是不需要的,造成性能浪费
因此,Vue3
在编译阶段,做了进一步优化。主要有如下:
- diff算法优化
- 静态提升
- 事件监听缓存
- SSR优化
diff算法优化
vue3
在diff
算法中相比vue2
增加了静态标记
关于这个静态标记,其作用是为了会发生变化的地方添加一个flag
标记,下次发生变化的时候直接找该地方进行比较
下图这里,已经标记静态节点的p
标签在diff
过程中则不会比较,把性能进一步提高
关于静态类型枚举如下
1 | export const enum PatchFlags { |
静态提升
Vue3
中对不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用
这样就免去了重复的创建节点,大型应用会受益于这个改动,免去了重复的创建操作,优化了运行时候的内存占用
1 | <span>你好</span> |
没有做静态提升之前
1 | export function render(_ctx, _cache, $props, $setup, $data, $options) { |
做了静态提升之后
1 | const _hoisted_1 = /*#__PURE__*/_createVNode("span", null, "你好", -1 /* HOISTED */) |
静态内容_hoisted_1
被放置在render
函数外,每次渲染的时候只要取 _hoisted_1
即可
同时 _hoisted_1
被打上了 PatchFlag
,静态标记值为 -1 ,特殊标志是负整数表示永远不会用于 Diff
事件监听缓存
默认情况下绑定事件行为会被视为动态绑定,所以每次都会去追踪它的变化
1 | <div> |
没开启事件监听器缓存
1 | export const render = /*#__PURE__*/_withId(function render(_ctx, _cache, $props, $setup, $data, $options) { |
开启事件侦听器缓存后
1 | export function render(_ctx, _cache, $props, $setup, $data, $options) { |
上述发现开启了缓存后,没有了静态标记。也就是说下次diff
算法的时候直接使用
SSR优化
当静态内容大到一定量级时候,会用createStaticVNode
方法在客户端去生成一个static node,这些静态node
,会被直接innerHtml
,就不需要创建对象,然后根据对象渲染
1 | div> |
编译后
1 | import { mergeProps as _mergeProps } from "vue" |
二、源码体积
相比Vue2
,Vue3
整体体积变小了,除了移出一些不常用的API,再重要的是Tree shanking
任何一个函数,如ref
、reavtived
、computed
等,仅仅在用到的时候才打包,没用到的模块都被摇掉,打包的整体体积变小
1 | import { computed, defineComponent, ref } from 'vue'; |
三、响应式系统
vue2
中采用 defineProperty
来劫持整个对象,然后进行深度遍历所有属性,给每个属性添加getter
和setter
,实现响应式
vue3
采用proxy
重写了响应式系统,因为proxy
可以对整个对象进行监听,所以不需要深度遍历
- 可以监听动态属性的添加
- 可以监听到数组的索引和数组
length
属性 - 可以监听删除属性
关于这两个 API 具体的不同,我们下篇文章会进行一个更加详细的介绍