1.底层响应式原理不同
1. Vue2
Vue2使用的是Object.defineProperty()来劫持各个属性的setter/getter,在数据发生变化的时候通知订阅者更新视图。
缺点:
- 无法检测到对象的属性添加和删除
- 无法检测到数组的内部变化,因此Vue2通过重写数组方法来实现数组的响应式
- 需要遍历整个对象,如果对象嵌套过深,需要递归遍历,性能会下降
2. Vue3
Vue3使用的是Proxy来劫持整个对象,从而实现响应式。Proxy可以直接监听对象和数组的变化,不需要遍历整个对象,性能会更好。
后续官方提出了ref API,可以监听基本数据类型的变化。
- 引用数据类型:Proxy
- 基本数据类型:使用对象的属性来监听——> RefImpl
优点:
- 可以监听到对象和数组的变化
- 可以监听到对象属性的添加和删除
- 浅层监听,只有当对象的深层属性被使用到时才会递归遍历监听。减少了性能开销。
2. 详解响应式
1.ref & reactive
ref和reactive是Vue3中用来创建响应式数据的两个API。
- ref:用来创建基本数据类型的响应式数据。我们在使用ref时,需要通过.value来访问和修改数据。但是我们在使用模板时,不需要.value,Vue3会自动帮我们处理。本质上Vue3在编译时会使用一个函数(proxyRefs)来自动解包处理。
- reactive:用来创建引用数据类型的响应式数据。本质上是只监听一层,如果该对象的深层属性被访问到了,就会递归监听,否则不会递归监听。reactive的返回值是一个Proxy对象。
2.toRefs & toRef
toRefs和toRef是Vue3中用来将响应式对象的属性转换为响应式引用的API。
- toRef:将响应式对象的属性转换为响应式引用。可以将Proxy对象的某个属性映射为一个ref对象,在proxy对象中修改该属性,ref对象也会跟着改变。
- toRefs:将响应式对象的属性转换为响应式引用。可以将Proxy对象的全部属性映射为一个ref对象,在proxy对象中修改某个属性,ref对象也会跟着改变。返回一个对象,对象的属性名和proxy对象的属性名相同,属性值是一个ref对象。
3.watch & watchEffect
watch是Vue3中用来监听响应式数据变化的API。
- watch会返回一个停止监听的函数,调用该函数可以停止监听。
- watch第一个参数可以传递ref或者reactive,也可以传递一个getter函数或者ref或者reactive的数组。注意,在监听ref.value或者reactive的属性时,必须使用getter函数。
- onCleanUp 是Vue3中用来清除副作用的API。在watch的回调函数中,可以使用onCleanUp来清除副作用。这个函数会在watch监听的值发生变化,当前回调函数被执行之前执行或者组件销毁时执行。用于清除上一次的副作用。它会被挂载到当前watch回调函数的第三个参数上。同时Vue3也将这个API单独暴路出来,可以单独使用。在Vue3.5+,官方暴露出了onWatcherCleanup函数,专门用于在watch回调函数中清除副作用。作用和onCleanUp一样。但是这个函数只能同步使用,不能异步使用。即不能在await之后使用。
watchEffect 是 Vue 3 中引入的一个新特性,它会自动响应式地运行副作用,并且不需要显式指定依赖。是Vue3新增。本质上就是使用了Vue3响应式中的effect函数。
- watchEffect会返回一个停止监听的函数,调用该函数可以停止监听。
- watchEffect的回调函数会在组件渲染时执行一次,然后每次依赖的响应式数据发生变化时执行一次。
- watchEffect的回调函数中,不需要显式指定依赖,它会自动收集依赖。
- watchEffect回调函数会被挂载一个参数,该参数就是onCleanUp函数,用于清除副作用。也可以使用onWatcherCleanup函数。
- 返回一个停止监听的函数,调用该函数可以停止监听。
- 如果需要同步触发watchEffect,可以使用一个新的API watchSyncEffect 来代替。
4.effect
effect是Vue3中用来创建副作用的API。在Vue3中,effect函数会自动收集依赖,并在依赖发生变化时重新执行副作用。effect函数会返回一个停止监听的函数,调用该函数可以停止监听。
- effect函数的回调函数会在组件渲染时执行一次,然后每次依赖的响应式数据发生变化时执行一次。
- 在Vue3中使用了一个WeakMap 来存储effect函数和响应式数据的关系。当响应式数据发生变化时,会触发effect函数的回调函数。这个map的结构为:
1 | Map { |
本质上是一个三级嵌套的Map结构,第一层是响应式数据,第二层是响应式数据的属性,第三层是effect函数。当响应式数据发生变化时,会触发effect函数的回调函数。在effect函数的回调函数中,会调用effect的run方法再次执行对应的getter函数,从而触发响应式数据的变化。
2.生命周期
Vue2 生命周期钩子 | Vue3 生命周期钩子(Options API) | Vue3 Composition API 等效写 | 法触发时机说明 |
---|---|---|---|
beforeCreate | setup() | setup() | 组件实例初始化前(无法访问数据) |
created | setup() | setup() | 组件实例创建完成(可访问数据) |
beforeMount | onBeforeMount | onBeforeMount | 挂载到 DOM 前 |
mounted | onMounted | onMounted | 挂载到 DOM 后 |
beforeUpdate | onBeforeUpdate | onBeforeUpdate | 数据变化导致 DOM 更新前 |
updated | onUpdated | onUpdated | DOM 更新完成后 |
beforeDestroy | onBeforeUnmount(改名) | onBeforeUnmount | 组件卸载前 |
destroyed | onUnmounted(改名) | onUnmounted | 组件卸载后 |
errorCaptured | onErrorCaptured | onErrorCaptured | 捕获子孙组件错误时 |
renderTracked(新增) | onRenderTracked | onRenderTracked | 调试用:响应式依赖被追踪时 |
renderTriggered(新增) | onRenderTriggered | onRenderTriggered | 调试用:响应式依赖触发更新时 |
3.其他优化
1. 写法优化:Composition API 的革新
1. 与 Options API 对比
特性 | Options API (Vue2) | Composition API (Vue3) |
---|---|---|
代码组织 | 按选项(data、methods)分离 | 按功能逻辑聚合 |
逻辑复用 | Mixins(命名冲突风险) | 自定义 Hook 函数(函数式复用) |
TypeScript 支持 | 类型推导较弱 | 原生类型支持完善 |
代码可读性 | 简单场景直观,复杂场景分散 | 复杂场景更易维护 |
2. Composition API 核心优势
- 逻辑复用:将逻辑封装为自定义 Hook(替代 Mixins)。
- 代码组织:相关逻辑集中管理(如将数据请求与状态管理放在一起)。
- 更好的类型推导:函数式写法天然适合 TypeScript。
1 | // Composition API 示例:计数器逻辑复用 |
2.组件优化:更灵活强大的组件能力
1. Fragment(多根组件)
- Vue2 限制:组件模板必须单根节点,导致冗余包裹元素。
- Vue3 改进:支持多根节点,减少 DOM 层级。
1 | <!-- Vue3 合法模板 --> |
2. Teleport(传送门)
- 功能:将组件渲染到 DOM 任意位置(如全局弹窗)。
- 场景:解决样式隔离或 DOM 嵌套限制问题。
1 | <template> |
3. Suspense(异步组件)
- 功能:优雅处理异步组件加载状态。
- 场景:数据请求、动态导入组件时的加载中/错误状态。
1 | <template> |
3.Tree-shaking 支持:按需打包优化
1. 实现机制
模块化拆分:将 Vue 功能拆分为独立模块(如 v-model、transition、keep-alive)。
ES Module 输出:构建工具(如 Webpack、Rollup)可静态分析依赖关系。
2. 效果对比
场景 | Vue2 | Vue3 |
---|---|---|
全量引入 | ~20KB (全量) | ~12KB (核心运行时) |
使用部分功能 | 全量包含 | 仅打包使用到的模块 |
4.TypeScript 支持:全面拥抱类型系统
1. 核心改进
源码重写:Vue3 使用 TypeScript 编写,提供完整类型定义。
组合式 API 友好:函数式 API 天然适合类型推导。
模板类型检查:配合 Volar 插件实现模板内表达式类型检查。