一.响应式系统

vue2:
  • vue2的响应式系统基于object.defineProperty()实现,通过劫持对象属性的getter/setter来追踪依赖并触发更新。

  • vue2无法检测对象属性的动态添加或删除,因为object.defineProperty()必须在对象属性存在时才能劫持。

export default{
    data(){
        return{
            user:{name:'Tom'}
        }
    },
    created(){
        //无法触发响应式更新
        this.user.age = 18  //新属性没有经过 Object.defineProperty() 处理,因此 Vue 无法追踪它的变化。
        //正确方式,使用vue.set,它内部实现了:1.使用 Object.defineProperty() 为新属性创建 getter/setter;2.触发依赖更新
        this.$set(this.user,'age',18)
    }
}
  • vue2无法检测通过修改数组长度(如arr.length=0) 触发的变化(因为vue2的响应式系统不直接劫持数组的索引,也就是没有为数组的每个索引如arr[0]、arr[1]设置getter、setter,只能通过重写数组的变异方法(如 pushpopsplice 等)来劫持数组的变化。)

  • length 属性的修改属于 非变异操作,Vue2 没有对其进行特殊处理。因此,当你直接修改数组的 length 属性时,Vue2 无法捕获到这个变化。

export default {
  data() {
    return {
      items: [1, 2, 3]
    }
  },
  created() {
    // 无法触发响应式更新
    this.items.length = 0;
    
    // 正确方式:使用 splice,替代直接修改length
    this.items.splice(0);
  }
}
vue3:

vue3的响应式系统基于ES6 Proxy实现,相比于vue2的object.defineProperty()有了根本性的改进。它解决了vue2中存在的诸多限制,比如Vue3无需特殊API,直接添加属性即可触发响应式更新(user.age = 30)可以直接检测length的变化(item.length = 0),可以检测对象原型的修改等。

vue3使用proxy对象拦截对象的所有操作(读取、设置、删除等),从而实现对数据的全面监控。与vue2的defineProperty()相比,Proxy的优势在于:

  • 拦截范围更广:可以拦截对象属性的添加、删除、枚举、函数调用等操作;

  • 无需递归遍历:可以直接监听整个对象,而不需要递归处理所有嵌套属性,只有在访问嵌套对象时才会创建 Proxy

  • 按需响应式:只有访问到的嵌套对象才会被转换为响应式;

总结:
特性 Vue2 Vue3
实现原理 基于 Object.defineProperty () 基于 ES6 Proxy
深层响应式 需要递归遍历所有嵌套对象 原生支持深层响应式,无需特殊处理
动态属性添加 需使用 Vue.set () this.$set () 直接添加属性即可触发响应式更新
数组变异方法 需使用特定变异方法(如 push/pop) 无限制,Proxy 可拦截所有操作
局限性 无法检测对象属性的删除、新增,数组长度变化 无上述限制,提供更一致的响应式行为

二.组合式 API vs Options API

vue2(options API) 
  • 问题:逻辑分散在不同选项(data/methods/computed),大型项目难以维护
//vue2 Options API
export default{
    data(){
        return{
            count:0,
            loading:false,
            error:null
        }
    },
computed:{
   addCount(){
        return this.count*2;
   }
} ,
methods:{
    async fetchData(){
        this.loading = true;
        try{
            //fetch()是浏览器原生 API 用于向指定 URL 发送 HTTP 请求,并返回一个 Promise 对象
            //也可使用主流的axios  HTTP客户端
            const res = await fetch('/api/data')
            this.data = await res.json();// 解析响应为 JSON
        }catch(e){
            this.error = e;
        }finally{
            this.loading = false;
        }
    }
}   
}
vue3(组合式API)
  • 优势:按功能组织代码,逻辑内聚,提高复用性
//vue3(组合式API)
import {ref,computed,onMounted } from 'vue';

export default{
    setup{  //可使用 <script setup> 语法糖,代码更简洁
        //状态管理
        const count = ref(0);
        const addCount = computed(()=>count.value*2)
        //副作用逻辑
        const loading = ref(false);
        const error = ref(null);
        const data = ref(null);

        const fetchData = async() => {
            loading.value = true;
            try{
                const res = await fetch('/api/data');
                data.value = await res.json();
            }catch(e){
                error.value = e;
            }finally{
                loading.value = false;
            }
        }

        onMounted(()=>{
            fetchData();
        });
        return {count,doubleCount,loading, error,data,fetchData};
    }
}
总结:
特性 Vue2 Vue3
API 风格 Options API(data/methods/computed) 组合式 API(setup ()ref/reactive
逻辑复用 混入(Mixins)、高阶组件(HOC) 组合函数(Composition Functions)
代码组织 按选项拆分,逻辑分散 按功能组织,逻辑内聚
类型支持 TypeScript 支持有限 原生支持 TypeScript,类型推导更完善

三.生命周期钩子

Vue2 钩子 Vue3 等效钩子 说明
beforeCreate () { } setup() 组件初始化前执行
created () { } setup() 组件实例创建后执行
beforeMount () { } onBeforeMount (()=>{}) DOM 挂载前执行
mounted () { } onMounted (()=>{}) DOM 挂载后执行
beforeUpdate () { } onBeforeUpdate (()=>{}) 数据更新前执行
updated () { } onUpdated (()=>{}) 数据更新后执行
beforeDestroy () { } onBeforeUnmount (()=>{}) 组件销毁前执行
destroyed () { } onUnmounted (()=>{}) 组件销毁后执行
activated () { } onActivated (()=>{}) 缓存组件激活时执行
deactivated () { } onDeactivated (()=>{}) 缓存组件停用时执行
errorCaptured () { } onErrorCaptured (()=>{}) 捕获子组件错误

四.组件与模版

特性 Vue2 Vue3
多根节点(Fragment) 不支持,组件必须单个根元素 支持多根节点,无需包裹元素
Teleport 支持 <teleport> 组件,可将内容渲染到 DOM 任意位置
Suspense 支持 <Suspense> 组件,处理异步依赖
自定义指令 基于对象的 API 支持组合式 API,使用 setup ()
  • 多根节点:
//vue2必须有单个根元素
<template>
    <div>
        <header>...</header>
        <main>...</main>
        <footer>...</footer>
    </div>
</template>

//vue3 支持多根节点 可以有多个div并行
<template>
     <header>...</header>
     <main>...</main>
     <footer>...</footer>
</template>
  • teleport组件(传送门) vue3独有

作用:将组件内容渲染到 DOM 中的其他位置,而不必改变组件的逻辑结构。常见场景:模态框、悬浮提示、全屏组件等。

<template>
  <div>
    <button @click="showModal = true">Open Modal</button>
    
    <teleport to="body">
      <div v-if="showModal" class="modal">
        <p>Modal Content</p>
        <button @click="showModal = false">Close</button>
      </div>
    </teleport>
  </div>
</template>

<script setup>
import { ref } from 'vue';

    const showModal = ref(false);
</script>
  •  Suspense 组件(异步组件处理)vue3独有

作用:处理异步组件和异步数据加载的状态管理,提供加载中、加载失败等过渡状态。

<template>
  <Suspense>
    <!-- 加载完成后显示 -->
    <template #default>
      <AsyncComponent />
    </template>
    
    <!-- 加载中显示 -->
    <template #fallback>
      <div>Loading...</div>
    </template>
  </Suspense>
</template>

<script>
import { defineAsyncComponent } from 'vue';

const AsyncComponent = defineAsyncComponent(() =>
  import('./components/AsyncComponent.vue')
);

export default {
  components: { AsyncComponent }
}
</script>

五.性能优化

特性 Vue2 Vue3
编译优化 全量更新,虚拟 DOM 比对开销大 基于动态标记(PatchFlag)的靶向更新,仅更新变化部分
Tree-shaking 不支持,所有功能打包进应用 支持按需引入,体积更小
渲染函数 性能一般 优化的渲染函数,执行效率更高
内存占用 较高,每个实例维护多个 watcher 降低约 40% 内存占用

六.Typescript支持

特性 Vue2 Vue3
类型支持 需要额外插件(vue-class-component) 原生支持,无需额外配置
类型推导 有限,需要手动声明类型 基于组合式 API 的自动类型推导
DefineComponent 提供类型增强的组件定义函数

示例:

<template>
    <div>
        <p>{{ message }}</p>
        <p>{{ count }}</p>
        <button @click="increment">+1</button>
  </div>
</template>

<script setup lang="ts">
    import { ref, computed, onMounted, defineProps, defineEmits } from 'vue';
    //定义props类型
    const props = defineProps<{
        message:string;
        initialCount?:number;
     }>()
    //定义emits类型
    const emits = defineEmits<{
       ( event:'change',value:number ):void;
    }>();
    //响应式数据带类型
    const count = ref<number>(props.initialCount||0);
    //计算属性
    const doubleCount = computed(()=> count.value*2)
    //方法
    const increment = ()=>{
           count.value++;
            emits('change',count.value);
    }
</script>

七.其他重要特性

特性 Vue2 Vue3
全局 API 全局配置(Vue.config) 创建应用实例(createApp),避免全局污染
v-model 单个 v-model 绑定 支持多个 v-model 绑定,自定义修饰符
Composition API 提供 setup ()、provide/inject、customRef 等
生态系统 成熟但部分库需适配 新库(如 Vue Router 4、Pinia)优先支持 Vue3
  • 全局API

vue2:

  • 全局构造函数:使用vue构造函数创建应用实例,所有配置直接挂载到全局vue对象上;

  • 全局污染:任何组件都可以访问和修改全局配置,导致潜在的冲突;

//vue2
import Vue from 'vue';
import App from './App.vue' ;
import VueRouter from 'vue-router';
import MyButton from './components/MyButton.vue';

// 全局安装插件
Vue.use(VueRouter);

// 创建路由实例
const router = new VueRouter({...});

// 全局注册组件
Vue.component('MyButton', MyButton);

//全局配置(影响所有实例)
   Vue.config.productionTip = false;
   Vue.prototype.$http = axios;  //挂载全局属性
//创建根实例,在根实例中使用路由
    new Vue({
        router,
        render: h => h(App)
    }).$mount('#app')

vue3:

  • 应用实例隔离:使用createApp创建独立的应用实例,所有配置仅作用于当前实例。

  • 避免全局污染:每个应用实例有自己的配置,不同应用可共存且互不影响。

//Vue3
import {createApp} from 'vue';
import App from './App.vue';
import router from './router';
import MyButton from './components/MyButton.vue';
//创建应用实例
const app = createApp(App);

// 全局注册组件
app.component('MyButton', MyButton);

// 实例级安装插件
app.use(router);

//实例配置(仅影响当前应用)
app.config.productionTip = false;
app.provide('$http',axios);//提供依赖注入,替代vue.prototype

//挂载应用
app.mount('#app');
V-model:

Vue2 和 Vue3 在 v-model 指令上的实现和功能有显著差异,主要体现在 语法糖本质组件间通信方式多 v-model 支持 和 修饰符机制 等方面。

vue2:
  • 本质:v-model语法糖,在原生元素和组件中有不同表现;
    • 原生元素:绑定value属性并监听input事件

    • 组件:绑定 value 属性并监听 input 事件。

//vue2原生元素
<input v-model = 'message'>
//等价于
<input :value = 'message' @input="message=$event.target.value">

//vue2组件
<CustomInput v-model="message">
<!-- 等价于 -->
<CustomInput :value="message" @input="message = $event">
vue3:
  • 本质v-model 是 语法糖,但默认绑定 modelValue 属性并监听 update:modelValue 事件,更灵活且支持多 v-model。

    • 原生元素:行为与 vue2 一致(绑定 value 和 input)。

    • 组件:绑定 modelValue 属性并监听 update:modelValue 事件。

<!-- Vue3:原生元素 -->
<input v-model="message">
<!-- 等价于 -->
<input :value="message" @input="message = $event.target.value">

<!-- Vue3:组件 -->
<CustomInput v-model="message">
<!-- 等价于 -->
<CustomInput :modelValue="message" @update:modelValue="message = $event">
多 v-model 绑定(Vue3 新增)

Vue3 支持在同一组件上使用 多个 v-model 绑定,每个绑定对应不同的 prop事件

<!-- 父组件 -->
<CustomForm 
  v-model:username="username" 
  v-model:password="password" 
/>

<!-- 等价于 -->
<CustomForm 
  :username="username" 
  @update:username="username = $event"
  :password="password" 
  @update:password="password = $event" 
/>
//子组件:CustomForm.vue
<template>
  <div>
    <input 
      :value="username" 
      @input="$emit('update:username', $event.target.value)" 
    >
    <input 
      :value="password" 
      @input="$emit('update:password', $event.target.value)" 
    >
  </div>
</template>

<script>
export default {
  props: {
    username: String,
    password: String
  },
  emits: ['update:username', 'update:password']
}
</script>
修饰符对比:
修饰符 Vue2 Vue3 说明
.trim 自动过滤输入首尾空格
.number 将输入转换为数字类型
.lazy 监听 change 事件而非 input
.debounce Vue2/3 均不内置,需自定义
自定义修饰符 需插件 Vue3 原生支持组件级自定义修饰符
总结:
特性 Vue2 Vue3
默认 prop / 事件 value / input modelValue / update:modelValue
多 v-model 支持 不支持 支持
自定义修饰符 需插件实现 原生支持,通过 modelModifiers
emits 选项 不强制 推荐声明,增强类型安全
与表单元素绑定 直接绑定 value 组件中需绑定 modelValue
Tree-shaking支持:

vue3实现了更好的tree-shaking支持,核心库被拆分成多个包,使得打包工具可以移除未使用的代码,减小应用体积。

在vue2中,即使没有使用某些功能(如v-model、过渡动画),它们也会被打包到最终的构建中。而Vue3的API大多数都是从vue包中单独引入的函数,如果没有使用就不会被打包。

总结:

特性 Vue2 Vue3
响应式原理 Object.defineProperty() Proxy
API 风格 Options API 组合式 API
多根节点 不支持 支持
Teleport/Suspense 内置组件
全局 API 全局污染 应用实例隔离
v-model 单个绑定 多个绑定 + 自定义修饰符
TypeScript 支持 需额外插件 原生支持
逻辑复用 Mixins(命名冲突、类型问题) 组合函数(灵活、类型安全)
性能 全量虚拟 DOM 比对 PatchFlag 靶向更新

面试题:Vue2与Vue3有哪些区别?

回答重点:(1)性能提升(响应式系统)(2)组合式API (3)Teleport组件 (4)片段(Fragment)多根节点 (5)Typescript支持(6)Suspence组件 (7)Tree-shaking支持

Logo

欢迎加入西安开发者社区!我们致力于为西安地区的开发者提供学习、合作和成长的机会。参与我们的活动,与专家分享最新技术趋势,解决挑战,探索创新。加入我们,共同打造技术社区!

更多推荐