Vue3 彻底重写了响应式系统,核心从 Vue2 的Object.defineProperty切换为ES6 Proxy,解决了 Vue2 响应式的三大痛点:无法监听对象新增属性、无法监听数组索引修改、无法监听Map/Set等集合类型。同时,Vue3 通过ref和reactive两个核心 API,分别处理基本类型和复杂对象的响应式,兼顾灵活性和易用性。
<script setup>
import { ref, reactive, toRefs, watchEffect, computed } from 'vue'
// 1. ref:用于处理基本类型(String/Number/Boolean等)
// 原理:ref内部封装了一个包含value属性的对象,通过Proxy监听value的变化
const username = ref('vue-user')
// 注意:在<script setup>中修改ref值需加.value,模板中Vue会自动解包,无需手动添加
console.log(username.value) // 输出:vue-user
// 2. reactive:用于处理复杂对象(Object/Array等)
// 原理:直接对原始对象创建Proxy,监听对象属性的新增、删除、修改
const userInfo = reactive({
age: 20,
address: {
city: 'Beijing',
district: 'Haidian' // 嵌套对象也能被监听
},
hobbies: ['reading', 'coding'] // 数组同样支持响应式
})
// 3. toRefs:将reactive对象转换为ref对象集合,解决解构后丢失响应性的问题
// 场景:当需要在模板中单独使用对象的某个属性,或传递给子组件时
const { age, address, hobbies } = toRefs(userInfo)
// 注意:toRefs返回的ref对象与原reactive对象共享状态,修改会同步影响原对象
age.value += 1 // userInfo.age也会变为21
// 4. watchEffect:自动追踪依赖的响应式数据,无需手动指定监听源
// 原理:执行函数时,会收集函数内使用的所有响应式数据作为依赖,当依赖变化时重新执行函数
const watchHandler = watchEffect(() => {
// 此处依赖了username、age、address.city三个响应式数据
console.log(`用户名:${username.value},年龄:${age.value},城市:${address.value.city}`)
// 实际场景可用于更新DOM、发送请求、操作本地存储等
})
// 5. 停止监听:当组件卸载或不需要监听时,调用返回的函数停止,避免内存泄漏
// 场景:如监听某个弹窗的显示状态,弹窗关闭后需停止监听
// watchHandler() // 调用即停止
// 6. 复杂场景支持:Vue3响应式系统对对象新增属性、数组操作的完美支持
const updateUser = () => {
// 修改ref对象
username.value = 'new-vue-user'
// 修改reactive对象的现有属性
userInfo.age += 1 // 等同于age.value += 1
// 新增对象属性(Vue2不支持,Vue3可直接监听)
userInfo.gender = 'male'
// 修改嵌套对象属性
userInfo.address.city = 'Shanghai' // 无需像Vue2那样使用$set
// 数组操作(push/pop/splice等方法均支持响应式)
userInfo.hobbies.push('hiking') // 数组变化会触发依赖更新
// 直接修改数组索引(Vue2不支持,Vue3可监听)
userInfo.hobbies[0] = 'writing'
}
// 7. computed:基于响应式数据派生新值,支持缓存和读写
const fullAddress = computed({
// get方法:计算派生值,依赖变化时重新计算
get() {
return `${address.value.city} ${address.value.district}`
},
// set方法:可选,支持修改computed值,需同步更新依赖的响应式数据
set(newValue) {
const [city, district] = newValue.split(' ')
address.value.city = city
address.value.district = district
}
})
// 使用:读取时无需加.value(模板中),脚本中需加.value
console.log(fullAddress.value) // 输出:Beijing Haidian
fullAddress.value = 'Guangzhou Tianhe' // 会触发set方法,更新address
</script>
<template>
<div class="user-card">
<h3>{{ username }}</h3>
<p>年龄:{{ age }}</p>
<p>完整地址:{{ fullAddress }}</p>
<p>爱好:{{ hobbies.join(', ') }}</p>
<p v-if="userInfo.gender">性别:{{ userInfo.gender }}</p>
<button @click="updateUser">更新用户信息</button>
</div>
</template>
<style scoped>
.user-card {
padding: 20px;
border: 1px solid #eee;
border-radius: 8px;
max-width: 400px;
margin: 20px auto;
}
button {
margin-top: 15px;
padding: 8px 16px;
background: #42b983;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>Vue3 响应式系统的设计不仅解决了 Vue2 的历史问题,还通过ref与reactive的分工,让开发者能根据数据类型灵活选择 API,同时配合watchEffect、computed等工具,大幅提升了响应式开发的效率和可维护性。

