本文详细解释Vue2、Vue3二个版本的区别
| 对比维度 | Vue2 选项式 API | Vue3 组合式 API(setup 函数 / setup 语法糖) |
|---|
| 代码组织方式 | 按选项类型组织:data、methods、computed、watch 等分散在不同选项中 | 按逻辑功能组织:相关变量、函数、计算属性和监听器集中在一起 |
| 访问响应式数据 | 通过 this 访问 data、methods 等成员 | 使用 ref 和 reactive 创建响应式数据,通过 .value 访问 ref 值(在 JS 中) |
| 模板中使用变量 | 直接使用 data 中定义的属性,无需 .value | 模板中使用 ref 时自动解包,无需 .value |
| 入口函数 | 组件选项对象本身(如 export default { ... }) | setup() 函数作为组件的入口,或使用 setup 语法糖自动执行 |
| 语法糖支持 | 不支持 setup | 支持 setup语法糖,省去 setup() 函数和 return,更简洁 |
| this 指向 | 在 methods、computed 中可使用 this 指向组件实例 | 例 setup() 中没有 this,完全基于函数式编程思想 |
| 代码拆分灵活性 | 难以将同一功能的逻辑拆分到多个文件 | 可轻松将逻辑提取为独立的组合函数(composables)并跨组件复用 |
- 逻辑按类型组织:data、methods、computed、watch分散在不同区域
<!-- Vue 2 -->
<template>
<div>
<h2>计数器: {{ count }}</h2>
<p>双倍值: {{ doubleCount }}</p>
<button @click="increment">+1</button>
<input v-model="search" placeholder="搜索用户" />
<ul>
<li v-for="user in filteredUsers" :key="user.id">
{{ user.name }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
count: 0,
search: '',
users: [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
]
}
},
methods: {
increment() {
this.count++
}
},
computed: {
doubleCount() {
return this.count * 2
},
filteredUsers() {
return this.users.filter(u =>
u.name.toLowerCase().includes(this.search.toLowerCase())
)
}
},
watch: {
count(newVal) {
console.log('count changed:', newVal)
}
}
}
</script>
<!-- Vue 3 - 标准 setup -->
<template>
<div>
<h2>计数器: {{ count }}</h2>
<p>双倍值: {{ doubleCount }}</p>
<button @click="increment">+1</button>
<input v-model="search" placeholder="搜索用户" />
<ul>
<li v-for="user in filteredUsers" :key="user.id">
{{ user.name }}
</li>
</ul>
</div>
</template>
<script>
import { ref, computed, watch } from 'vue'
export default {
setup() {
// 计数逻辑
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
const increment = () => {
count.value++
}
// 搜索逻辑
const search = ref('')
const users = ref([
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
])
const filteredUsers = computed(() => {
return users.value.filter(u =>
u.name.toLowerCase().includes(search.value.toLowerCase())
)
})
// 监听
watch(count, (newVal) => {
console.log('count changed:', newVal)
})
// 返回给模板使用的变量和方法
return {
count,
doubleCount,
increment,
search,
filteredUsers
}
}
}
</script>
<!-- Vue 3 - 使用 <script setup>(顶级语法糖) -->
<script setup>
import { ref, computed, watch } from 'vue'
// 计数逻辑
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
const increment = () => count.value++
// 搜索逻辑
const search = ref('')
const users = ref([
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
])
const filteredUsers = computed(() => {
return users.value.filter(u =>
u.name.toLowerCase().includes(search.value.toLowerCase())
)
})
// 监听
watch(count, (newVal) => {
console.log('count changed:', newVal)
})
</script>
<template>
<div>
<h2>计数器: {{ count }}</h2>
<p>双倍值: {{ doubleCount }}</p>
<button @click="increment">+1</button>
<input v-model="search" placeholder="搜索用户" />
<ul>
<li v-for="user in filteredUsers" :key="user.id">
{{ user.name }}
</li>
</ul>
</div>
</template>
import Vue from 'vue'
import App from './App.vue'
new Vue({
render: h => h(App)
}).$mount('#app')
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
| 钩子 | 执行时机 | 是否常用 |
|---|
| beforeCreate | 实例初始化后,数据观测和事件配置之前 | ❌ 少用 |
| created | 实例创建完成,数据已响应式,但 DOM 未挂载 | ✅ 常用于请求数据 |
| beforeMount | 模板编译完成,即将挂载到 DOM | ❌ 较少使用 |
| mounted | 组件已挂载到 DOM,可操作真实 DOM | ✅ 最常用之一 |
| beforeUpdate | 数据更新时,虚拟 DOM 重新渲染前 | ✅ 可用于调试 |
| updated | 虚拟 DOM 重新渲染并应用到 DOM 后 | ✅ 注意避免无限循环 |
| beforeDestroy | 组件销毁前,仍可访问实例 | ✅ 清理定时器、事件监听等 |
| destroyed | 组件已销毁,所有绑定解除,子实例也被销毁 | ✅ 常用 |
<!-- Vue 2 -->
<template>
<div id="app">
<h2 v-if="show">当前计数:{{ count }}</h2>
<button @click="count++">+1</button>
<button @click="show = false">销毁组件</button>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
count: 0,
show: true,
timer: null
}
},
beforeCreate() {
console.log('🚀 beforeCreate: 实例还未初始化')
// 此时 this.$el 不存在,data 未代理
},
created() {
console.log('✅ created: 实例创建完成', this.count)
// 可以发起网络请求
this.timer = setInterval(() => {
console.log('定时器运行中...')
}, 1000)
},
beforeMount() {
console.log('📦 beforeMount: 模板编译完成,DOM 未挂载')
},
mounted() {
console.log('🎉 mounted: 组件已挂载到页面', this.$el)
// 可操作 DOM
},
beforeUpdate() {
console.log('🔄 beforeUpdate: 数据变化,视图更新前')
},
updated() {
console.log('📈 updated: 视图已更新')
},
beforeDestroy() {
console.log('🧹 beforeDestroy: 组件即将销毁')
// 必须手动清除副作用
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
},
destroyed() {
console.log('💀 destroyed: 组件已完全销毁')
}
}
</script>
| 钩子 | 执行时机 | 是否常用 |
|---|
| setup | 组件创建前,替代 beforeCreate 和 created,是 Composition API 的入口 | ✅ 必用,用于初始化数据和逻辑 |
| onBeforeMount | 模板编译完成,即将挂载到 DOM | ❌ 较少使用,但可用于调试 |
| onMounted | 组件已挂载到 DOM,可安全操作真实 DOM | ✅ 最常用之一,常用于请求数据、绑定事件、初始化第三方库 |
| onBeforeUpdate | 数据更新时,虚拟 DOM 重新渲染前 | ✅ 可用于调试或获取更新前的 DOM 状态 |
| onUpdated | 虚拟 DOM 重新渲染并应用到 DOM 后 | ✅ 注意避免在其中修改数据导致无限循环 |
| onBeforeUnmount | 组件销毁前,实例仍可用,可清理副作用(如定时器、事件监听 | ✅ 常用于资源清理,防止内存泄漏 |
| onUnmounted | 组件已完全卸载,所有绑定解除,子实例也被销毁 | ✅ 常用于确认销毁逻辑或日志记录 |
<!-- Vue 3 - 使用 <script setup> -->
<script setup>
import {
ref,
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted
} from 'vue'
const count = ref(0)
const show = ref(true)
let timer = null
// 模拟副作用:定时器
timer = setInterval(() => {
console.log('[Timer] 运行中...')
}, 1000)
// === 生命周期钩子 ===
console.log('🚀 setup: 组件开始初始化')
onBeforeMount(() => {
console.log('📦 onBeforeMount: 即将挂载')
})
onMounted(() => {
console.log('🎉 onMounted: 组件已挂载', document.getElementById('app'))
})
onBeforeUpdate(() => {
console.log('🔄 onBeforeUpdate: 视图更新前')
})
onUpdated(() => {
console.log('📈 onUpdated: 视图已更新')
})
onBeforeUnmount(() => {
console.log('🧹 onBeforeUnmount: 组件即将卸载')
// 清理副作用
if (timer) {
clearInterval(timer)
timer = null
console.log('✅ 定时器已清除')
}
})
onUnmounted(() => {
console.log('💀 onUnmounted: 组件已完全卸载')
})
</script>
<template>
<div id="app">
<h2 v-if="show">当前计数:{{ count }}</h2>
<button @click="count++">+1</button>
<button @click="show = false">销毁组件</button>
</div>
</template>
| 功能 | Vue 2 | Vue 3 | 区别说明 |
|---|
| 初始化前 | beforeCreate | setup() 开始处 | setup() 替代了 beforeCreate 和 created |
| 初始化后 | created | setup() 结束前 | Vue 3 推荐在 setup() 中统一处理初始化逻辑 |
| 挂载前 | beforeMount | onBeforeMount() | 名称一致,但 Vue 3 需导入使用 |
| 挂载后 | mounted | onMounted() | 用法相同,功能不变 |
| 更新前 | beforeUpdate | onBeforeUpdate() | 无本质变化 |
| 更新后 | updated | onUpdated() | 注意避免在其中修改响应式数据 |
| 销毁前 | beforeDestroy | onBeforeUnmount() | 命名变更,语义更准确(“卸载”而非“销毁”) |
| 销毁后 | destroyed | onUnmounted() | 同上,名称更规范 |