• 一文掌握Vue3:深度解读Vue3新特性、Vue2与Vue3核心差异以及Vue2到Vue3转型迭代迁移重点梳理与实战


    在这里插入图片描述

    每次技术革新均推动着应用性能与开发体验的提升。Vue3 的迭代进步体现在性能优化、API重构与增强型TypeScript支持等方面,从而实现更高效开发、更优运行表现,促使升级成为保持竞争力与跟进现代前端趋势的必然选择。本文深度解读Vue3 响应式数据data、生命周期钩子、计算属性computed、监听watch、模板语法、指令、事件处理、组件注册、Props、emits、Mixins等新特性、Vue2与Vue3核心差异以及Vue2到Vue3转型迭代迁移重点梳理。

    Vue3 在Vue2的基础上实现了重大技术飞跃和性能提升,为开发体验和应用性能带来了诸多实质性改进。首先,Vue3 引入了全新的Composition API,它允许开发者以更灵活、可复用的方式组织逻辑,提高了代码的可读性和维护性。其次,Vue3 对虚拟DOM进行了重构,优化了编译器和运行时性能,大幅提升了大型应用的渲染速度。同时,Vue3支持Tree-Shaking,有助于减小打包体积。另外,Vue3增强了类型推断能力,更好地兼容TypeScript,提升了开发过程中的静态检查体验。因此,升级至Vue3有助于提高开发效率、优化应用性能,同时也为未来项目发展打下坚实基础。

    一、Vue2和Vue3主要的区别

    Vue.js 从其2.x版本进化到3.x版本,经历了一系列的重大改进和重构,以下是Vue2和Vue3之间主要的区别以及过渡历史变迁的详解:

    核心架构的变革

    1. 响应式系统
      • Vue2:基于Object.defineProperty()实现的观察者模式,只能递归地遍历并转换对象属性,但无法检测到新增或删除的属性。
      • Vue3:采用ES6的Proxy对象代替defineProperty,实现了更高效且完整的对象代理,能深度监听对象的变化,包括属性的添加和删除。

    API 设计

    1. 组件选项API vs. 组合式API
      • Vue2:采用的是选项式API(Options API),如datamethodscomputedwatch等都是独立的对象属性。
      • Vue3:引入了新的 Composition API,允许开发者通过函数的方式组织逻辑,使得代码逻辑复用性更强,尤其在大型应用中组件间的状态管理和逻辑共享更为便捷。与React Hooks设计思想相近,拉近了Vue与React语言编程方式,降低了Vue与React语言学习的成本

    生命周期钩子

    • Vue2:提供一系列生命周期钩子函数,如createdmounted等。
    • Vue3:生命周期钩子名称前缀增加了on,例如onMounted,并在Composition API中以函数形式使用,需要导入对应的生命周期钩子。

    模板语法增强

    • Vue3在模板语法上做了优化,例如移除了部分限制,使模板更灵活。

    指令和插槽

    • Vue3:更新了v-model的工作方式,支持更多的用法和场景,同时v-bindv-on简化为:prop@event,指令也有所调整,比如v-slot改为#slot

    构建工具和生态系统

    • Vue3:Vite成为官方推荐的现代前端构建工具,它基于原生ES模块提供了更快的冷启动速度和热更新体验。

    性能优化

    • Vue3在内部进行了许多优化,提高渲染性能,减小体积,并支持Tree-Shaking,使得最终打包后的代码更轻量级。

    过渡类名更改

    • Vue3中的一些过渡类名被重新命名以适应新的过渡系统。

    生态迁移

    • Vue3推出的同时,相关的生态也在逐步升级,例如Vuex和Vue Router都有对应的Vue3版本。

    总的来说,Vue3不仅在底层响应式系统上进行了革新,而且在框架设计层面提出了更现代化的编程范式,旨在解决大型应用开发中的复杂性和可维护性问题,同时也保持了对Vue2的良好兼容性,为开发者提供了平滑的迁移路径。随着时间推移,Vue3逐渐成熟并获得了广泛的应用和认可。

    二、Vue3关键新特性要点

    Vue3关键新特性要点的主要围绕以下几个方面:

    1、响应式数据

    Vue2 中的 data 是一个函数,用于返回一个包含响应式属性的对象:

    // Vue2
    export default {
      data() {
        return {
          message: 'Hello, Vue2!',
          user: {
            name: 'John Doe',
            age: 30
          }
        };
      }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在 Vue3 中,有两种方式来声明响应式数据,取决于你是否使用 Composition API:

    使用 Options API(Vue3 中仍然保留,但不是最佳实践):
    // Vue3 (Options API)
    import { reactive } from 'vue';
    
    export default {
      setup() {
        const state = reactive({
          message: 'Hello, Vue3!',
          user: {
            name: 'John Doe',
            age: 30
          }
        });
    
        return {
          ...state
        };
      }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    使用 Composition API(Vue3 推荐做法):
    // Vue3 (Composition API)
    import { ref, reactive } from 'vue';
    
    export default {
      setup() {
        const message = ref('Hello, Vue3!');
        const user = reactive({
          name: 'John Doe',
          age: 30
        });
    
        return {
          message,
          user
        };
      }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 对于简单的数据类型(如字符串、数字、布尔值),我们可以使用 ref 创建响应式引用。
    • 对于复杂的对象或数组结构,应当使用 reactive 来创建响应式的代理对象。

    这两种方式都可以使数据具有响应性,但 refreactive 提供了更细粒度的控制和更灵活的组合能力。在 Composition API 中,setup 函数取代了 Vue2 中的生命周期钩子,成为处理所有组件初始化逻辑的地方,包括响应式状态的声明。

    2、生命周期钩子

    Vue2 中的生命周期钩子在 Vue3 中经历了较大的调整,主要是因为引入了 Composition API。以下是 Vue2 中常见的生命周期钩子及它们在 Vue3 中的对应转换:

    Vue2 的生命周期钩子:

    export default {
      data() {
        return {
          count: 0
        };
      },
      beforeCreate() {
        // 实例初始化后,数据观测和事件配置之前
      },
      created() {
        // 实例创建完成后,数据观测和方法都已生效
      },
      beforeMount() {
        // 模板编译/渲染之前,还未生成render树
      },
      mounted() {
        // 虚拟DOM替换为真实DOM,组件已挂载完成
      },
      beforeUpdate() {
        // 数据更新时,虚拟DOM重新渲染之前
      },
      updated() {
        // 数据更新后,DOM已重新渲染完成
      },
      beforeUnmount() {
        // 卸载组件之前
      },
      unmounted() {
        // 卸载组件完成
      }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    Vue3 中的生命周期钩子(Composition API):

    import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue';
    
    export default {
      setup() {
        const count = ref(0);
    
        // 创建前(Vue2的beforeCreate + created合并)
        onBeforeMount(() => {
          // 组件挂载前,响应式数据准备就绪
        });
    
        // 挂载时
        onMounted(() => {
          // 组件已挂载到DOM,$el等DOM相关属性可用
        });
    
        // 更新前
        onBeforeUpdate(() => {
          // 数据更新导致组件即将重新渲染
        });
    
        // 更新后
        onUpdated(() => {
          // 组件已重新渲染完成
        });
    
        // 卸载前
        onBeforeUnmount(() => {
          // 组件即将卸载
        });
    
        // 卸载后
        onUnmounted(() => {
          // 组件已完成卸载
        });
    
        return {
          count
        };
      }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    注意Vue3中setup函数是在beforeCreate之前执行,并且没有this上下文,所有数据都是通过Composition API管理。同时,Vue3中移除了beforeDestroy,改为onBeforeUnmount,并新增了unmounted对应Vue2的destroyed钩子。

    3、计算属性computed

    Vue2 中的计算属性 computed 是通过 computed 选项定义的,而 Vue3 中同样有 computed,但是其用法随着 Composition API 的引入发生了变化。

    Vue2 中的 computed 示例:

    // Vue2
    export default {
      data() {
        return {
          firstName: 'John',
          lastName: 'Doe'
        };
      },
      computed: {
        fullName() {
          return `${this.firstName} ${this.lastName}`;
        }
      }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    Vue3 中的 computed 示例:

    当使用 Options API(非 Composition API)时,Vue3 中的 computed 与 Vue2 类似:

    // Vue3 (Options API)
    import { computed } from 'vue';
    
    export default {
      data() {
        return {
          firstName: 'John',
          lastName: 'Doe'
        };
      },
      computed: {
        fullName() {
          return `${this.firstName} ${this.lastName}`;
        }
      }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    然而,当使用 Composition API 时,Vue3 中的 computed 定义方式有所不同:

    // Vue3 (Composition API)
    import { ref, computed } from 'vue';
    
    export default {
      setup() {
        const firstName = ref('John');
        const lastName = ref('Doe');
    
        const fullName = computed(() => {
          return `${firstName.value} ${lastName.value}`;
        });
    
        return {
          firstName,
          lastName,
          fullName
        };
      }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在 Composition API 中,计算属性 fullNamecomputed 函数包裹,并且依赖于其他响应式变量(这里是指 firstNamelastName.value)。计算属性本身也是一个响应式对象,它的值会在依赖发生变化时自动重新计算。

    4、 监听watch

    Vue2 中的 watch 是用来监视数据变化并触发回调的,而在 Vue3 中,watch 的用法也有所改变,尤其是在使用 Composition API 的场景下。

    Vue2 中的 watch 示例:

    // Vue2
    export default {
      data() {
        return {
          count: 0,
          name: ''
        };
      },
      watch: {
        count(newCount, oldCount) {
          console.log(`Count changed from ${oldCount} to ${newCount}`);
        },
        name(newValue, oldValue) {
          console.log(`Name changed from ${oldValue} to ${newValue}`);
        },
        // 监视对象的某个深层属性
        someObject: {
          handler(newValue, oldValue) {
            // ...
          },
          deep: true // 开启深度监听
        }
      }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    Vue3 中的 watch 示例:

    使用 Options API:
    // Vue3 (Options API)
    import { watch } from 'vue';
    
    export default {
      data() {
        return {
          count: 0,
          name: ''
        };
      },
      setup() {
        // 观察响应式数据
        const count = ref(0);
        const name = ref('');
    
        // 定义 watch
        watch(count, (newCount, oldCount) => {
          console.log(`Count changed from ${oldCount} to ${newCount}`);
        });
        watch(name, (newValue, oldValue) => {
          console.log(`Name changed from ${oldValue} to ${newValue}`);
        });
    
        // 深度观察对象
        const someObject = reactive({ nested: { value: 0 } });
        watch(
          () => someObject.nested.value,
          (newValue, oldValue) => {
            // ...
          },
          { deep: true }
        );
    
        return {
          count,
          name,
          someObject
        };
      }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    使用 Composition API:
    // Vue3 (Composition API)
    import { ref, watch, reactive } from 'vue';
    
    export default {
      setup() {
        const count = ref(0);
        const name = ref('');
        const someObject = reactive({ nested: { value: 0 } });
    
        // 定义 watch
        watch(count, (newCount, oldCount) => {
          console.log(`Count changed from ${oldCount} to ${newCount}`);
        });
    
        watch(name, (newValue, oldValue) => {
          console.log(`Name changed from ${oldValue} to ${newValue}`);
        });
    
        // 深度观察对象
        watch(
          () => someObject.nested.value,
          (newValue, oldValue) => {
            // ...
          },
          { deep: true }
        );
    
        return {
          count,
          name,
          someObject
        };
      }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    在 Vue3 中,watch 是一个函数,它接收一个 getter 函数(用于获取被监视的值)、一个回调函数(当值发生改变时调用),以及可选的配置对象。如果需要深度观察对象,需在配置对象中设置 { deep: true }

    5、模板语法

    Vue2 和 Vue3 的模板语法大部分保持一致,但也有一些差异,尤其是关于指令和特性绑定的部分。以下是一些主要区别:

    Vue2 模板示例:

    
    <template>
      <div>
        
        {{ message }}
    
        
        <img :src="imageUrl" alt="Vue Logo">
    
        
        <p v-if="seen">现在你看到我了p>
        <p v-else>现在你看不到我p>
    
        
        <ul>
          <li v-for="(item, index) in items" :key="item.id">
            {{ item.name }} - Index: {{ index }}
          li>
        ul>
    
        
        <input v-model="searchText">
    
        
        <button @click="greet">点击打招呼button>
      div>
    template>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    Vue3 模板示例:

    
    <template>
      <div>
        
        {{ message }}
    
        
        <img :src="imageUrl" alt="Vue Logo">
    
        
        <p v-if="seen">现在你看到我了p>
        <p v-else>现在你看不到我p>
    
        
        <ul>
          <li v-for="(item, index) in items" :key="item.id">
            {{ item.name }} - Index: {{ index }}
          li>
        ul>
    
        
        <input v-model:value="searchText">
    
        
        <button @click="greet">点击打招呼button>
    
        
        <child-component #default="{ item }">
          {{ item.name }}
        child-component>
      div>
    template>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    在Vue3中,v-model 有了更严格的语法要求,需要配合.value使用,以反映Composition API中响应式属性的工作方式。此外,Vue3中的作用域插槽( Scoped Slots )使用了改进过的v-slot语法,可以省略