# 1. Vue2 options Api 和 Vue3 conponents Api 对比

# Vue2 options Api示例图

  • 通过选项(data, methods, computed 等)组织代码:
export default {
  data() { /* ... */ },
  methods: { /* ... */ },
  computed: { /* ... */ }
};
1
2
3
4
5

# Vue3 conponents Api

  • 通过 setup() 函数按逻辑组织代码(类似 React Hooks):
import { ref, computed } from 'vue';

export default {
  setup() {
    const count = ref(0);
    const double = computed(() => count.value * 2);
    return { count, double };
  }
};
1
2
3
4
5
6
7
8
9

当这个组件的代码超过几百行时,这时增加或者修改某个需求, 就要在 data、methods、computed 以及 mounted 中反复的跳转,这其中的的痛苦写过的都知道。

vue2.x 版本给出的解决方案就是 Mixin,但是也会有相对应的问题
1、命名冲突问题
2、不清楚暴露出来的变量的作用
3、逻辑重用到其他 component 经常遇到问题

但本来mixin本来的作用就是共享组件之间的可复用功能

# 2.Vue2 组件只允许有一个根节点,Vue3允许有多个

// Vue2
<template>
    <div>
        <span></span>
        <span></span>
    </div>
</template>
1
2
3
4
5
6
7
// Vue3
<template>
    <span></span>
    <span></span>
</template>
1
2
3
4
5

# 3.响应式数据拦截监听方法比较

Vu2采用的是 Object.defineProperty, Vue3采用的是 Proxy

Object.defineProperty 虽然已经能够实现双向绑定了,但是他还是有缺陷的。
1、只能对属性进行数据劫持,所以需要深度遍历整个对象
2、对于数组不能监听到数据的变化
3、Proxy 的第二个参数可以有 13 种拦截方法,比 Object.defineProperty() 要更加丰富
4、Proxy 的兼容性不如 Object.defineProperty() 不能使用 polyfill 来处理兼容性

而Object.defineProperty这个对于数组缺陷可以通过vue提供的set方法去解决

Proxy能够原生支持监听数组变化,并且可以直接对整个对象进行拦截

# 4.生命周期钩子对比

Vue 3 的生命周期钩子名称略有变化(更语义化):

Vue 2 Vue 3 触发时机说明
beforeCreate 无直接替代(在 setup 中执行) 组件实例初始化前(无法访问数据)
created 无直接替代(在 setup 中执行) 组件实例初始化后(可以访问数据)
beforeMount onBeforeMount 组件挂载前(DOM 未完全渲染)
mounted onMounted 组件挂载后(DOM 已完全渲染)
beforeUpdate onBeforeUpdate 组件更新前(DOM 未完全更新)
updated onUpdated 组件更新后(DOM 已完全更新)
beforeDestroy onBeforeUnmount 组件销毁前(DOM 未完全销毁)
destroyed onUnmounted 组件销毁后(DOM 已完全销毁)
  • Vue3中的setip生命周期中没有this,setup 在生命周期 beforecreate 前执行,此时 vue 对象还未创建,因无法使用我们在 vue2.x 常用的 this。

# 5.watch 和 watchEffect 区别

我们已经大概知道了 watch 和 watchEffect 的用法,那么它们之间的区别相信大家也了解了一些,这里我们总结一下它们之间的区别。
1、watch 和 watchEffect 都能监听响应式数据的变化,不同的是它们监听数据变化的方式不同。
2、watch 会明确监听某一个响应数据,而 watchEffect 则是隐式的监听回调函数中响应数据。
3、watch 在响应数据初始化时是不会执行回调函数的,watchEffect 在响应数据初始化时就会立即执行回调函数。

监听器的回调函数中或取 DOM,这个时候的 DOM 是更新前,那怎么取更新后的dom?
给监听器多传递一个参数选项即可:flush: 'post'。watch 和 watchEffect 同理。

watch(source, callback, {
  flush: 'post'
})
watchEffect(callback, {
  flush: 'post'
})
1
2
3
4
5
6

# 6.watch监听响应式对象中的某个属性

# 6-1、使用 getter 函数的形式

watch(
  () => number.count,
  (newValue, oldValue) => {
   
  }
);
1
2
3
4
5
6

# 6-2、使用watchEffect

const number = reactive({ count: 0 });
const countAdd = () => {
  number.count++;
};
watchEffect(()=>{
  console.log("新的值:", number.count);
})
1
2
3
4
5
6
7

# 7.Vue3 Diff算法和 Vue2 的区别

编译阶段的优化:
1、事件缓存:将事件缓存(如: @click),可以理解为变成静态的了
2、静态提升:第一次创建静态节点时保存,后续直接复用
3、添加静态标记:给节点添加静态标记,以优化 Diff 过程

由于编译阶段的优化,除了能更快的生成虚拟 DOM 以外,还使得 Diff 时可以跳过"永远不会变化的节点",Diff 优化如下:

Vue2 是全量 Diff。
Vue3 是静态标记 + 非全量 Diff, 使用最长递增子序列优化了对比流程。

对比项 Vue 2 Vue 3
Diff 策略 双端交叉比较(头尾指针) 快速 Diff + 最长递增子序列(LIS)
静态优化 Patch Flag 标记静态节点
复用机制 依赖 key 的全量比较 使用 Rollup 进行生产打包
移动次数 可能较多 最小化(LIS 优化)
性能 较慢 更快(2~5 倍优化)

# 8.Pinia和Vuex对比

  • Pinia和Vuex一样都是是vue的全局状态管理器。其实Pinia就是Vuex5

pinia中没有了mutationsmodules,使用Vuex的时候每次修改state的值都需要调用mutations,而pinia则不再需要mutations,同步异步都可在actions进行操作

Pinia没有modules,如果想使用多个store,直接定义多个store

# 9.获取数据生命周期对比

# Vue 2 (Options API)

  • 在获取数据后,对数据的处理如果不涉及DOM,可在 created 阶段获取,毕竟速度更快一些
  • 如果数据涉及到DOM的处理,则要在mounted阶段获取数据
created() {
  // 常见的数据获取位置(不依赖 DOM)
  fetch('https://api.example.com/posts')
}
1
2
3
4

# Vue 3 (Composition API)

  1. setup() 函数内部(相当于 created 阶段)

setup() 是组合式 API 的入口,在组件创建时同步执行(早于 beforeMount 和 mounted)。

这是推荐的数据获取位置,类似于 Vue 2 的 created。

  1. onMounted(如果需要依赖 DOM)

通过 onMounted 钩子可以在 DOM 挂载后执行操作,类似于 Vue 2 的 mounted。

 setup() {
    // 推荐:在 setup 中直接获取数据(类似 created)
    fetch('https://api.example.com/posts')

    // 如果需要 DOM,使用 onMounted
    onMounted(() => {
      console.log('DOM 已挂载');
    });
  }
1
2
3
4
5
6
7
8
9

# 10.数据定义对比

# Vue 2

data 必须是一个函数,返回一个对象: 所有响应式数据需在 data 函数中定义,Vue 会自动将其转换为响应式对象(基于 Object.defineProperty)。

export default {
  data() {
    return {
      count: 0,
      message: "Hello Vue2"
    };
  }
};
1
2
3
4
5
6
7
8

模板中使用,直接通过 this.count 访问。

# Vue 3

refreactive 显式定义响应式数据: Vue 3 的 Composition API 需要手动声明响应式数据:

  • ref: 用于基本类型(如 number, string)或对象,通过 .value 访问值。

  • reactive: 用于对象或数组,直接访问属性。

import { ref, reactive } from 'vue';

export default {
  setup() {
    const count = ref(0); // 基本类型
    const state = reactive({ message: "Hello Vue3" }); // 对象

    return { count, state };
  }
};
1
2
3
4
5
6
7
8
9
10

模板中使用,在模板中自动解包 ref,无需 .value

<template>
  <div>{{ count }}</div> <!-- 自动解包,无需 count.value -->
  <div>{{ state.message }}</div>
</template>
1
2
3
4

# 11.全局 API 变化

  • Vue 2: 全局 API 挂载在 Vue 构造函数上(如 Vue.component, Vue.directive)。

  • Vue 3: 使用 createApp 创建实例,全局 API 通过实例调用:

// Vue 3
import { createApp } from 'vue';
const app = createApp(App);
app.component('MyComponent', MyComponent);
app.mount('#app');
1
2
3
4
5

# 12.新增Teleport(传送门)

Vue 3 新增 Teleport:可以将组件渲染到 DOM 的任意位置(如全局弹窗):

<template>
  <Teleport to="body">
    <div class="modal">内容</div>
  </Teleport>
</template>
1
2
3
4
5

在这个例子中,<Teleport> 组件会将内部的 <div class="modal"> 渲染到 <body> 标签中,而不是在当前组件的 DOM 结构中。

  • 使用场景
  1. 模态框(Modal)
  2. 通知(Notification)
  3. 全局工具提示(Tooltip)
  4. 浮动菜单或弹窗(Dropdown)

# to 属性

to 属性指定了 Teleport 内容的目标位置。它可以是一个 CSS 选择器字符串,也可以是一个 DOM 元素。

<teleport to="#app">
  <div>内容将被渲染到 #app 元素中</div>
</teleport>
1
2
3

# 多个 Teleport 到同一个目标

如果多个 Teleport 组件指向同一个目标位置,它们的内容会按照在 DOM 中的顺序依次渲染。

<template>
  <div>
    <teleport to="#target">
      <div>第一个内容</div>
    </teleport>
    <teleport to="#target">
      <div>第二个内容</div>
    </teleport>
  </div>
</template>
1
2
3
4
5
6
7
8
9
10

# 限制与注意事项

  1. 状态依然在父组件
  • 虽然 DOM 渲染位置发生了变化,但组件的状态、事件等逻辑仍然属于父组件。
  • 父组件销毁时,Teleport 的内容也会被销毁。
  1. 动态目标

目标容器可以是动态创建的,但需要确保容器在 Teleport 渲染之前存在。

  1. 与 CSS 的交互

Teleport 的内容脱离了父组件的 DOM 层级,因此需要确保样式在目标容器中也适用。例如,全局样式表需要覆盖 Teleport 渲染的内容。

# 13.v-model 的变化

  • Vue 2: 一个组件仅支持一个 v-model。

在 Vue 2 中,v-model 主要用于输入元素 (如 <input><textarea><select>

<input v-model="value"></input>
1
  • Vue 3: 支持多个 v-model,并可自定义修饰符:

在 Vue 3 中,v-model 也可以在自定义组件上使用,并且可以自定义组件上的 modelValueupdate:modelValue 事件。

<!-- 父组件 -->
<ChildComponent v-model:title="pageTitle" v-model:content="pageContent" />
1
2

# 14.v-if/v-for 中的优先级

  • Vue 2: v-for 的优先级高于 v-if。

  • Vue 3: v-if 的优先级高于 v-for,避免逻辑冲突。

# 15.事件总线(Event Bus)

  • Vue 2: 常用 new Vue() 实例作为事件总线。

  • Vue 3: 推荐使用第三方库(如 mitt)或 provide/inject 替代。

mitt 是一个非常轻量级的发布/订阅库,适用于 Vue 3。 npm install mitt

  • 创建一个事件总线:
// event-bus.js
import mitt from 'mitt';
 
const emitter = mitt();
export default emitter;
1
2
3
4
5
  • 触发事件
// SomeComponent.vue
<template>
  <button @click="sendEvent">Send Event</button>
</template>
 
<script>
import emitter from './event-bus';
 
export default {
  methods: {
    sendEvent() {
      emitter.emit('custom-event', { message: 'Hello from SomeComponent!' });
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • 监听事件
// AnotherComponent.vue
<template>
  <div>{{ message }}</div>
</template>
 
<script>
import { onMounted, onUnmounted } from 'vue';
import emitter from './event-bus';
 
export default {
  data() {
    return {
      message: ''
    };
  },
  mounted() {
    this.listener = emitter.on('custom-event', (event) => {
      this.message = event.message;
    });
  },
  unmounted() {
    emitter.off('custom-event', this.listener);
  }
}
</script>
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
lastUpdate: 4/30/2025, 5:22:00 PM