Vue3 对比 Vue2 的优化
源码优化
-
使用 monorepo 管理代码
将各模块拆分到不同的 package 中,每个 package 有各自的 API、类型定义和测试,使得模块拆分更加细化,模块之间的依赖也更加明确。
-
使用 TypeScript 开发代码
相比 Vue2 使用 flow 的优势:
- 提供了更好的类型推导
- 对 TypeScript 更加友好
性能优化
-
减少体积
- 移除一些冷门的 feature
- 引入 tree-shaking 技术
-
数据劫持优化
Vue2 采用
Object.defineProperty()
劫持数据的 getter 和 setter,即在对象上添加 get 和 set 方法。缺点:
- 无法知道用户具体要访问哪一个属性,如果要实现深度监听需要递归给每一个对象都添加 get 和 set 方法,这会带来相当大的性能负担
- 无法检测对象属性的新增和删除
Vue3 采用了
Proxy
劫持数据的 getter 和 setter。(Proxy 并不能监听到内部深层次的对象变化,因此 Vue3 的处理方式是在 getter 中去递归响应式,起到懒深度监听的效果)优点:
- 可以实现懒深度响应式,对性能有大幅度优化
-
编译优化
Vue2
- 静态标记:创建一个数组用于缓存静态节点,并且生成的 vnode 带有
inStatic: true
属性,用于在 diff 中跳过它们的对比
Vue3
- 静态提升:将静态节点提取到 render 函数外面,减少 vnode的创建带来的性能损耗
- 预字符串化:将大量连续的静态节点转换为字符串,既减少 vnode 创建过程,也可以减少代码体积
- 缓存事件处理函数:每次 render 函数执行过后,生成新的 vnode,对 vnode 的 props 中事件属性进行 patch 的时候,就直接取上一次缓存的函数,如果没有缓存,每次函数都是新的,引用不一致,会造成组件的更新
- Block tree:在模板编译过程中收集动态子节点,能够在 diff 过程中根据动态子节点数量更新
- PatchFlag:比较动态子节点时配合使用
patchFlag
比较动态属性
- 静态标记:创建一个数组用于缓存静态节点,并且生成的 vnode 带有
-
<slot>
优化在 Vue2 中,无论插槽内容是否改变,都会重新生成 vnode 节点。
在 Vue3 中,会将插槽分为静态插槽和动态插槽,组件更新时只会比较动态插槽。
语法优化
-
优化逻辑组织
Vue2 中使用的 Options API 是按照 methods、data 等进行分类。
缺点:
- 当组件变得越来越大时,一个组件可能有多个逻辑关注点,此时 Options API 就会造成很差的开发体验
Vue3 中提供了 Composition API。
优点:
- 可以将相关逻辑放在一块,无需在一个文件的多个地方 “反复横跳”
- Composition API 无需使用 this,因此对 tree-shaking 也更加友好
-
优化逻辑复用
Vue2 中使用 mixin 来保存共用的逻辑。
缺点:
- 每个 mixin 之间是无感的,这会导致多个 mixin 容易定义相同的变量,导致命名冲突
- 对于组件而言,如果在模板中使用不在当前组件中定义的变量,会导致数据来源不清晰的问题
Vue3 中通过自定义 hook 来保存共用的逻辑。
优点:
- 可以在引入的时候对同名变量重命名,以解决命名冲突的问题
- 可以通过查看具体使用了哪一个 hook 来追溯数据来源
- 可以向自定义 hook 传递参数来改变逻辑,而 mixin 不行