针对Vue2转Vue3开发者 / 刚接触框架的新手,快速掌握核心差异与实战技巧
一、生命周期对比(Vue2 vs Vue3)
1.1 生命周期对照表(含组合式API)
vue2 是选项式 API的写法,vue3 是支持选项式和组合式两种写法,
建议大家都用组合式(见第十条):
Vue 2 生命周期 | Vue 3 选项式 API | Vue 3 组合式 API | 解释 |
---|---|---|---|
beforeCreate | beforeCreate | setup() | 在实例初始化之后,数据观测 data 和 event/watcher 事件配置之前被调用。此阶段无法访问 data 中的数据和 methods 中的方法。 |
created | created | setup() | 实例已经创建完成之后被调用。在这一步,实例已经完成了数据观测 data 、property 和 method 的计算、watch/event 事件回调。然而,挂载阶段还没有开始,$el 属性目前不可用。 |
beforeMount | beforeMount | onBeforeMount | 在挂载开始之前被调用:相关的 render 函数首次被调用。此时模板还未挂载到页面上。 |
mounted | mounted | onMounted | 实例已经挂载之后调用。此时模板已经编译完成并挂载到页面上,可以访问 $el 元素。 |
beforeUpdate | beforeUpdate | onBeforeUpdate | 在数据更新之前被调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。 |
updated | updated | onUpdated | 由于数据更改导致的虚拟 DOM 重新渲染和打补丁之后调用。此时 DOM 已经更新,可以执行依赖于更新后的 DOM 的操作。 |
beforeDestroy | beforeUnmount | onBeforeUnmount | 在实例销毁之前调用。在这一步,实例仍然完全可用。在 Vue 3 中,“销毁” 概念改为 “卸载”。 |
destroyed | unmounted | onUnmounted | 在实例销毁之后调用。调用后,所有的事件监听器和子实例都已经被销毁。在 Vue 3 中对应实例卸载之后。 |
1.2 关键差异解析
钩子 | Vue2 | Vue3组合式API | 触发时机 |
---|---|---|---|
setup() | ❌ 无 | ✅ 核心入口 | 组件初始化时立即执行,替代beforeCreate和created |
onMounted | mounted | onMounted | 组件挂载完成后执行(可访问DOM) |
onUnmounted | destroyed | onUnmounted | 组件卸载后清理副作用 |
二、组件通信全方案(Vue3组合式API版)
2.1 父子组件通信(组合式API标准写法)
<!-- 父组件 Parent.vue -->
<template>
<Child
:msg="parentMsg"
@update="handleUpdate"
/>
</template>
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'
const parentMsg = ref('Hello Vue3!')
const handleUpdate = (newValue) => {
parentMsg.value = newValue
}
</script>
<!-- 子组件 Child.vue -->
<template>
<div>
{{ msg }}
<button @click="sendData">Update</button>
</div>
</template>
<script setup>
// Vue3组合式API核心写法
const props = defineProps(['msg'])
const emit = defineEmits(['update'])
const sendData = () => {
emit('update', 'New Value from Child')
}
</script>
2.2 新旧写法对比(表格版)
功能 | Vue2选项式API | Vue3组合式API |
---|---|---|
定义props | props: ['msg'] | defineProps(['msg']) |
定义事件 | emits: ['update'] | defineEmits(['update']) |
触发事件 | this.$emit('update', value) | emit('update', value) |
响应式数据 | data() { return { count: 0 } } | const count = ref(0) |
2.3 组合式API优势说明
<script setup>
语法糖
-
自动暴露顶层绑定
-
无需
return
模板可用数据 -
更简洁的API组织方式
defineProps/defineEmits
// 带类型提示的进阶写法
const props = defineProps({
msg: {
type: String,
required: true
}
})
const emit = defineEmits<{
(e: 'update', value: string): void
}>()
模板直接访问
<!-- 无需.value直接访问 -->
<div>{{ msg }}</div>
<!-- 方法直接调用 -->
<button @click="sendData">Send</button>
2.4 其他通信方式补充
快速访问子组件实例(模板ref)
<!-- 父组件 -->
<template>
<Child ref="childRef" />
<button @click="childRef.someMethod()">调用子组件方法</button>
</template>
<script setup>
import { ref } from 'vue'
const childRef = ref(null)
</script>
依赖注入(provide/inject)
// 祖先组件
import { provide, ref } from 'vue'
const counter = ref(0)
provide('globalCounter', counter)
// 后代组件
import { inject } from 'vue'
const counter = inject('globalCounter')
2.5 插槽进阶用法
作用域插槽示例:
<!-- 子组件 -->
<slot name="footer" :user="userData"></slot>
<!-- 父组件 -->
<template v-slot:footer="{ user }">
<div>{{ user.name }}</div>
</template>
三、响应式系统核心
3.1 ref vs reactive 对比
特性 | ref | reactive |
---|---|---|
支持类型 | 所有类型 | 对象/数组 |
模板访问 | 自动解包 | 直接访问 |
重新赋值 | ✅ 保持响应 | ❌ 丢失响应 |
解构响应 | 需.value访问 | 需toRefs包裹 |
3.2 最佳实践选择
// 基本类型用ref
const count = ref(0)
// 复杂对象用reactive
const user = reactive({
name: 'Alice',
age: 25
})
// 函数返回用ref保持响应
const state = ref({
data: null,
loading: true
})
四、异步更新队列与 nextTick 原理
4.1 异步更新机制
Vue3 通过异步更新队列优化 DOM 更新性能,其核心逻辑如下:
-
批量更新:数据变更时,Vue 不会立即更新 DOM,而是将变更推入队列,在下一个事件循环中批量执行17
-
去重优化:同一数据的多次修改仅保留最终值,避免重复计算8
-
执行时机:优先使用微任务(Promise/MutationObserver),降级为宏任务(setTimeout)
// 示例:多次修改仅触发一次更新
const count = ref(0)
count.value++ // 修改1
count.value++ // 修改2
// DOM 更新只执行一次
4.2 nextTick 核心作用
-
获取最新 DOM:在数据变更后,通过
nextTick
确保回调在 DOM 更新后执行79 -
实现原理:利用 Promise 微任务队列(优先级:Promise > MutationObserver > setTimeout)
import { nextTick } from 'vue'
async function updateData() {
data.value = '新值'
await nextTick()
console.log('DOM已更新:', document.getElementById('element'))
}
五、Vuex 状态管理(Vue3 适配版)
5.1 核心概念对比
概念 | 作用 | Vue3 特性 |
---|---|---|
State | 全局状态存储 | 支持 Composition API 访问 |
Mutations | 同步修改状态(原子操作) | 必须通过 commit 触发 |
Actions | 异步操作 + 提交 Mutations | 支持 async/await 语法 |
Getters | 计算属性派生 | 类似组件的 computed |
5.2 组合式 API 使用
// store.js
import { createStore } from 'vuex'
export default createStore({
state: { user: null },
mutations: {
SET_USER(state, payload) {
state.user = payload
}
},
actions: {
async fetchUser({ commit }) {
const user = await api.getUser()
commit('SET_USER', user)
}
}
})
// 组件中使用
import { useStore } from 'vuex'
setup() {
const store = useStore()
const user = computed(() => store.state.user)
const login = async () => {
await store.dispatch('fetchUser')
}
return { user, login }
}
六、Vue-Router 路由管理(Vue3 新版)
6.1 路由配置优化
// router.js
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
component: () => import('@/views/Home.vue'), // 动态导入
meta: { requiresAuth: true }
},
{
path: '/about',
component: () => import(/* webpackChunkName: "about" */ '@/views/About.vue')
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
6.2 路由守卫升级
// 全局前置守卫
router.beforeEach((to, from) => {
if (to.meta.requiresAuth && !store.state.user) {
return '/login'
}
})
// 组合式 API 中使用
import { onBeforeRouteLeave } from 'vue-router'
setup() {
onBeforeRouteLeave((to, from) => {
// 离开路由前的逻辑
})
}
七、Vue3 Diff算法优化
7.1 核心优化点图解
7.2 性能提升场景
-
列表头尾插入效率提升50%
-
大数据量列表渲染优化30%
-
静态节点标记跳过比对
八、综合性能优化策略
8.1 异步组件 + 路由懒加载
// 异步组件定义
const AsyncComp = defineAsyncComponent(() => import('./Component.vue'))
// 路由配置
{ path: '/dashboard', component: () => import('@/views/Dashboard.vue') }
8.2 Keep-alive 缓存策略
<router-view v-slot="{ Component }">
<keep-alive include="Home,About">
<component :is="Component" />
</keep-alive>
</router-view>
8.3 状态管理优化
-
使用 Pinia(Vuex 的替代方案)简化状态管理
-
模块化拆分 Store,避免单一文件过大
九、常见问题解决方案
9.1 DOM 操作时机问题
// 错误示例
data.value = '新值'
document.getElementById('element').textContent = data.value // 可能获取旧 DOM
// 正确方案
data.value = '新值'
nextTick(() => {
document.getElementById('element').textContent = data.value
})
9.2 路由参数监听
import { watch } from 'vue'
import { useRoute } from 'vue-router'
setup() {
const route = useRoute()
watch(
() => route.params.id,
(newId) => {
fetchData(newId)
}
)
}
十、Vue3新特性速览
10.1 Composition API 优势
// 更灵活的逻辑组织
export default {
setup() {
const count = ref(0)
// 可复用的逻辑片段
const { data } = useFetch('/api/data')
// 生命周期聚合
onMounted(() => {
console.log('组件已挂载')
})
return { count, data }
}
}
10.2 其他重要更新
特性 | Vue2 | Vue3改进 |
---|---|---|
响应式系统 | Object.defineProperty | Proxy实现 |
模板指令 | v-for优先级高于v-if | v-if优先级高于v-for |
Fragment组件 | ❌ 不支持 | ✅ 支持多根节点 |
Teleport组件 | ❌ 无 | ✅ 跨DOM层级传送内容 |
十一、TypeScript支持
11.1 类型声明示例
interface User {
id: number
name: string
}
const user = reactive<User>({
id: 1,
name: 'Alice'
})
11.2 组合式API类型推断
// 带类型提示的ref
const count = ref<number>(0)
// 类型安全的emit
const emit = defineEmits<{
(e: 'update', value: number): void
}>()
十二、生态工具链(官方工具推荐)
工具 | 用途 | 命令示例 |
---|---|---|
Vite | 极速开发环境 | npm create vite@latest |
Pinia | 新一代状态管理 | npm install pinia |
VueUse | 常用工具集合 | npm install @vueuse/core |
Vitest | 单元测试框架 | npm install -D vitest |
学习路线建议:先掌握组合式API -> 熟悉响应式原理 -> 实践常用生态工具 -> 深入性能优化技巧