目录
一、Vue 2 与 Vue 3 的关键差异
1.双向数据绑定原理:
Object.defineProperty() 的作用
Proxy API 简介
2.支持碎片(Fragment)
为什么支持碎片很重要?
如何实现的?
3.API类型
4.数据变量和方法的定义
5.生命周期钩子
Vue 2 生命周期钩子
Vue 3 生命周期钩子
两者之间主要的区别:
1. 引入方式
2. 使用场合
3. 响应式函数
4. 组合性
5. 生命周期顺序
6. 与模板的关系
6.父子传参
Vue 2 的父子传参
Vue 3 的父子传参
主要区别
7. 指令与插槽
8. main.js 文件的不同
二、Vue 3 的新特性
1. Fragments、Teleport 和 createRenderer
2. Composition API
3. 更好的 TypeScript 支持
4. 性能提升
5. 更易维护
三、非兼容变更
四、移除的 API
五、总结
一、Vue 2 与 Vue 3 的关键差异
1.双向数据绑定原理:
- Vue2:
使用Object.defineProperty() 对属性进行劫持,结合发布订阅模式实现数据响应
仅仅能监听属性,不支持对整个对象的监听,且不适用于数组
Object.defineProperty() 的作用
Object.defineProperty() 方法允许精确地添加或修改对象的属性。此方法接收三个参数:
- 要添加属性的对象。
- 属性的名称。
- 一个描述符对象,该对象指定了属性的值、可写性、可枚举性、可配置性等。
例如:
let data = { val: 0 };
Object.defineProperty(data, 'prop', {get: function() {return this.val;},set: function(newVal) {this.val = newVal;},enumerable: true,configurable: true
});
这个例子定义了一个名为 prop 的属性,它读取和设置内部 val 属性的值。
- Vue3:
采用ES6 Proxy API 对数据进行代理,实现更高效的数据监听
支持对对象和数组的监听,能够更细致地捕捉数据变化
Proxy API 简介
Proxy 是 ES6 引入的一种新的数据结构,它允许你创建一个对象的代理,从而在访问对象的属性或方法时进行拦截和自定义操作。Proxy 对象有两个参数:目标对象和处理器对象,处理器对象定义了当操作被执行时的自定义行为。
let target = { message: 'Hello' };
let handler = {get(target, property, receiver) {console.log(`Property ${property} has been read.`);return Reflect.get(target, property, receiver);},set(target, property, value, receiver) {console.log(`Property ${property} set to ${value}.`);return Reflect.set(target, property, value, receiver);}
};let proxy = new Proxy(target, handler);
proxy.message; // Property message has been read.
proxy.message = 'Hi'; // Property message set to Hi.
2.支持碎片(Fragment)
- Vue2:不支持多个根节点
- Vue3:支持多个根节点,增加了组件的灵活性
为什么支持碎片很重要?
更自然的模板结构:开发者可以更自然地编写模板,而不必为了满足单一根节点的限制而进行额外的包装。
避免不必要的 DOM 操作:不需要额外的包装元素,减少了 DOM 操作和潜在的性能影响。
更好的组件封装:组件可以返回多个独立的 DOM 元素,使得组件的封装和复用更加灵活。
提高代码的可维护性:模板结构更清晰,有助于提高代码的可读性和可维护性。
如何实现的?
Vue 3 使用了 Fragment 指令来实现对多个根节点的支持。在编译阶段,Vue 3 会将多个根节点的模板编译为一个 Fragment 节点,然后在渲染过程中,将 Fragment 节点拆分为多个独立的 DOM 元素。
3.API类型
- Vue2:采用选项式API,将逻辑分割成data、computed、methods等部分
- Vue3:采用组合式API,提供更灵活的逻辑组织方式
4.数据变量和方法的定义
Vue2:数据通过data()函数定义,方法在 methods 对象中定义
Vue3:引入setup()函数作为组件的入口点,集中定义数据和方法
5.生命周期钩子
Vue 2 生命周期钩子
Vue 2 的生命周期钩子分为几个阶段:
-
创建阶段:
beforeCreate:在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。created:在实例创建完成后被调用,此时,实例已完成数据观测、属性和方法的运算,$el属性还未显示出来。
-
挂载阶段:
beforeMount:在挂载开始之前被调用,相关的render函数首次被调用。mounted:在el被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子,此时可以访问到 DOM 元素。
-
更新阶段:
beforeUpdate:在数据变化之后,DOM 重新渲染之前调用,此时可以在这个钩子中进一步地更改状态。updated:在由于数据更改导致的虚拟 DOM 重新渲染和打补丁后调用。
-
销毁阶段:
beforeDestroy:在实例销毁之前调用,进行一些清理工作。destroyed:在实例销毁后调用。
Vue 3 生命周期钩子
Vue 3 引入了 Composition API,带来了一组新的生命周期钩子,这些钩子在 setup 函数中使用:
-
创建阶段:
setup:是 Composition API 的入口点,在组件实例创建之前执行,可以在这里声明响应式数据和函数。
-
挂载阶段:
onBeforeMount:在挂载开始之前被调用,与 Vue 2 的beforeMount钩子相对应。onMounted:在组件第一次挂载到 DOM 后调用,与 Vue 2 的mounted钩子相对应。
-
更新阶段:
onBeforeUpdate:在更新开始之前被调用,与 Vue 2 的beforeUpdate钩子相对应。onUpdated:在组件更新 DOM 后调用,与 Vue 2 的updated钩子相对应。
-
销毁阶段:
onBeforeUnmount:在组件销毁之前调用,与 Vue 2 的beforeDestroy钩子相对应。onUnmounted:在组件销毁后调用,与 Vue 2 的destroyed钩子相对应。
两者之间主要的区别:
1. 引入方式
-
Vue 2:
- 生命周期钩子是组件选项对象的一部分,不需要从外部引入。
-
Vue 3:
- 生命周期钩子函数需要从
vue包中显式引入。 - 使用组合式 API 时,这些生命周期钩子作为函数导入并在
setup函数内部使用。
- 生命周期钩子函数需要从
2. 使用场合
-
Vue 2:
- 生命周期钩子作为组件选项对象的属性来定义,例如
created(),mounted(), 等等。
- 生命周期钩子作为组件选项对象的属性来定义,例如
-
Vue 3:
- 在
setup函数内部使用生命周期钩子,这是 Composition API 的一部分。
- 在
3. 响应式函数
-
Vue 2:
- 在
created或beforeMount钩子中定义的响应式数据和函数可以直接通过this访问。
- 在
-
Vue 3:
- 在
setup函数中定义的响应式数据和函数需要从setup返回,以便在模板或其他 Composition API 钩子中使用。
- 在
4. 组合性
-
Vue 2:
- 选项式 API 中的生命周期钩子是分离的,每个钩子处理不同的逻辑。
-
Vue 3:
- 使用组合式 API 时,生命周期钩子可以与响应式状态、计算属性和其他函数一起组合在
setup函数中。
- 使用组合式 API 时,生命周期钩子可以与响应式状态、计算属性和其他函数一起组合在
5. 生命周期顺序
- Vue 2 和 Vue 3:
- 生命周期的顺序和阶段在两者中保持一致,但是 Vue 3 引入了新的
setup阶段,这是在beforeCreate之前执行的。
- 生命周期的顺序和阶段在两者中保持一致,但是 Vue 3 引入了新的
6. 与模板的关系
-
Vue 2:
- 生命周期钩子中定义的响应式数据和函数自动与模板关联。
-
Vue 3:
- 在
setup函数中返回的响应式数据和函数被模板使用。
- 在
6.父子传参
Vue 2 的父子传参
在 Vue 2 中,父子组件之间的参数传递主要依靠 props 和事件:
父传子(Props):
- 父组件通过
props向子组件传递数据。 - 子组件通过声明
props来接收来自父组件的数据。
// 子组件
export default {props: ['message']
};
子传父(事件):
- 子组件使用
this.$emit方法触发事件,并将数据传递给父组件。 - 父组件在子组件标签上监听这些事件
// 子组件
this.$emit('my-event', someData);// 父组件
<ChildComponent @my-event="handleEvent"/>
Vue 3 的父子传参
Vue 3 保留了 Vue 2 的 props 和事件机制,但在组合式 API(setup 函数)中引入了 emit 函数,提供了一种新的方式来处理子组件向父组件传递数据:
-
父传子(Props):
与 Vue 2 类似,父组件通过props向子组件传递数据。 -
子传父(事件):
在 Vue 3 的setup函数中,emit函数作为参数传入,可以直接使用,而不需要通过this.$emit。
// 子组件
import { defineComponent, ref } from 'vue';export default defineComponent({setup(props, { emit }) {// 直接使用 emit 触发事件function sendMessage() {emit('my-event', 'Hello from child');}return { sendMessage };}
});
- 父组件的事件监听方式不变,仍然使用
@event语法。
// 父组件
<ChildComponent @my-event="handleEvent"/>
主要区别
- 子传父(组合式 API 中的 emit 函数):
- Vue 3 中的
setup函数为子组件提供了一个新的emit函数,使得事件触发更加直观和方便。 - 这种新的事件触发方式仅在组合式 API 中使用,选项式 API 中的事件触发方式与 Vue 2 相同。
- Vue 3 中的
7. 指令与插槽
- Vue 2:
slot指令直接使用,v-for优先级高于v-if。 - Vue 3:
slot指令需要使用v-slot的形式,v-for和v-if可以更自然地结合使用。
8. main.js 文件的不同
- Vue 2:使用构造函数的形式创建 Vue 应用。
- Vue 3:使用工厂函数的形式,允许更灵活的应用创建方式。
二、Vue 3 的新特性
1. Fragments、Teleport 和 createRenderer
- Fragments:允许组件拥有多个根节点,增加了组件的灵活性。
- Teleport:能够将组件的某些部分传送到 DOM 中的其他位置,解决了 modals、toast 等元素的定位问题。
- createRenderer:提供了创建自定义渲染器的能力,可以将 Vue 的开发模型扩展到其他平台。
2. Composition API
Composition API 提供了一种更灵活、更易于维护的方式来组织组件逻辑,支持逻辑的复用。它包括 ref、reactive、computed、watch 等响应式函数,以及 setup() 函数作为组件的入口点。
3. 更好的 TypeScript 支持
Vue 3 重写时考虑了 TypeScript 的支持,使得开发者能够享受到更好的类型推断和类型检查。
4. 性能提升
Vue 3 在虚拟 DOM 的重写、组件初始化和编译模板方面进行了优化,提高了性能。Vue 3 的响应式系统和组件初始化过程也得到了改进,使得组件更新更快。
5. 更易维护
Vue 3 的架构改进使得代码更易于维护,同时提供了更好的逻辑组合和复用。
三、非兼容变更
Vue 3 带来了一些非兼容的变更,如全局 API 的更改、模板指令的变化、组件定义方式的调整等。这些变更虽然可能需要开发者对现有代码进行调整,但它们旨在提供更好的性能和更灵活的用法。
四、移除的 API
Vue 3 移除了一些 Vue 2 的 API,如 keyCode 修饰符、$on、$off 和 $once 实例方法、过滤器(filter)等。对于这些移除的 API,Vue 3 提供了替代方案,以帮助开发者平滑迁移。
五、总结
Vue 3 带来了许多令人兴奋的新特性和改进,包括组合式 API、更好的 TypeScript 支持、性能提升等。同时,它也进行了一些非兼容的变更和移除了一些 API。对于 Vue 开发者来说,Vue 3 提供了更多的可能性和更好的开发体验。