# 1. Vue2 options Api 和 Vue3 conponents Api 对比
# Vue2 options Api示例图


- 通过选项(data, methods, computed 等)组织代码:
export default {
data() { /* ... */ },
methods: { /* ... */ },
computed: { /* ... */ }
};
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 };
}
};
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>
2
3
4
5
6
7
// Vue3
<template>
<span></span>
<span></span>
</template>
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'
})
2
3
4
5
6
# 6.watch监听响应式对象中的某个属性
# 6-1、使用 getter 函数的形式
watch(
() => number.count,
(newValue, oldValue) => {
}
);
2
3
4
5
6
# 6-2、使用watchEffect
const number = reactive({ count: 0 });
const countAdd = () => {
number.count++;
};
watchEffect(()=>{
console.log("新的值:", number.count);
})
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中没有了mutations
和modules
,使用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')
}
2
3
4
# Vue 3 (Composition API)
- setup() 函数内部(相当于 created 阶段)
setup() 是组合式 API 的入口,在组件创建时同步执行(早于 beforeMount 和 mounted)。
这是推荐的数据获取位置,类似于 Vue 2 的 created。
- onMounted(如果需要依赖 DOM)
通过 onMounted 钩子可以在 DOM 挂载后执行操作,类似于 Vue 2 的 mounted。
setup() {
// 推荐:在 setup 中直接获取数据(类似 created)
fetch('https://api.example.com/posts')
// 如果需要 DOM,使用 onMounted
onMounted(() => {
console.log('DOM 已挂载');
});
}
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"
};
}
};
2
3
4
5
6
7
8
模板中使用,直接通过 this.count
访问。
# Vue 3
ref
和 reactive
显式定义响应式数据:
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 };
}
};
2
3
4
5
6
7
8
9
10
模板中使用,在模板中自动解包 ref
,无需 .value
。
<template>
<div>{{ count }}</div> <!-- 自动解包,无需 count.value -->
<div>{{ state.message }}</div>
</template>
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');
2
3
4
5
# 12.新增Teleport(传送门)
Vue 3 新增 Teleport
:可以将组件渲染到 DOM 的任意位置(如全局弹窗):
<template>
<Teleport to="body">
<div class="modal">内容</div>
</Teleport>
</template>
2
3
4
5
在这个例子中,<Teleport>
组件会将内部的 <div class="modal">
渲染到 <body>
标签中,而不是在当前组件的 DOM 结构中。
- 使用场景
- 模态框(Modal)
- 通知(Notification)
- 全局工具提示(Tooltip)
- 浮动菜单或弹窗(Dropdown)
# to 属性
to 属性指定了 Teleport 内容的目标位置。它可以是一个 CSS 选择器字符串,也可以是一个 DOM 元素。
<teleport to="#app">
<div>内容将被渲染到 #app 元素中</div>
</teleport>
2
3
# 多个 Teleport 到同一个目标
如果多个 Teleport 组件指向同一个目标位置,它们的内容会按照在 DOM 中的顺序依次渲染。
<template>
<div>
<teleport to="#target">
<div>第一个内容</div>
</teleport>
<teleport to="#target">
<div>第二个内容</div>
</teleport>
</div>
</template>
2
3
4
5
6
7
8
9
10
# 限制与注意事项
- 状态依然在父组件
- 虽然 DOM 渲染位置发生了变化,但组件的状态、事件等逻辑仍然属于父组件。
- 父组件销毁时,Teleport 的内容也会被销毁。
- 动态目标
目标容器可以是动态创建的,但需要确保容器在 Teleport 渲染之前存在。
- 与 CSS 的交互
Teleport 的内容脱离了父组件的 DOM 层级,因此需要确保样式在目标容器中也适用。例如,全局样式表需要覆盖 Teleport 渲染的内容。
# 13.v-model 的变化
- Vue 2: 一个组件仅支持一个 v-model。
在 Vue 2 中,v-model 主要用于输入元素 (如 <input>
、<textarea>
和 <select>
)
<input v-model="value"></input>
- Vue 3: 支持多个 v-model,并可自定义修饰符:
在 Vue 3 中,v-model
也可以在自定义组件上使用,并且可以自定义组件上的 modelValue
和 update:modelValue
事件。
<!-- 父组件 -->
<ChildComponent v-model:title="pageTitle" v-model:content="pageContent" />
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;
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>
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>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25