在 Vue 3 中,响应式系统是其核心特性之一,它使得数据的变化能够自动触发视图的更新。
reactive()
基本概念
- 作用:用于创建一个响应式的对象。如果对这个对象的属性进行修改,会自动触发视图的更新。
- 可接收的数据类型:只能定义对象类型的响应式数据。使用
reactive()定义基本类型会报错,要用ref。 - 语法:
let 响应式对象= reactive(源对象) - 返回值:一个
Proxy的实例对象,简称:响应式对象。 - 类型:
function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
使用reactive()创建对象类型的响应式数据:
<template>
<div>
<p>姓名: {{ person.name }}</p>
<p>年龄: {{ person.age }}</p>
</div>
</template>
<script setup lang="ts">
import { reactive } from 'vue';
const person = reactive({
name: '张三',
age: 36
})
console.log(person)
// 报错:类型“number”的参数不能赋给类型“object”的参数。
let count = reactive(0)
</script>
控制台打印:

直接修改响应式对象的属性值,会触发依赖这些属性的组件重新渲染:
// 直接修改,立即响应
person.name = '李四'
person.age = 24
// 使用方法,调用方法后再响应
const changePerson = () => {
person.name = '李四'
person.age = 24
}
reactive()深层响应性
- 对象的嵌套属性也具有响应式
- 响应式转换是“深层”的:它会影响到所有嵌套的属性。一个响应式对象也将深层地解包任何
ref属性,同时保持响应性。
- 响应式转换是“深层”的:它会影响到所有嵌套的属性。一个响应式对象也将深层地解包任何
<template>
<div>
<p>b: {{ obj.a.b }}</p>
<p>d: {{ obj.a.c.d }}</p>
<button @click="changeB">修改b</button>
<button @click="changeD">修改d</button>
</div>
</template>
<script setup lang="ts">
import { reactive } from 'vue';
const obj = reactive({
a: {
b: 10,
c: {
d: 20
}
},
});
const changeB = () => {
obj.a.b++
}
const changeD = () => {
obj.a.c.d++
}
</script>
在这个例子中,修改嵌套对象的属性也会触发响应式更新。
不管数据嵌套的有多深,reactive()一定会把数据变成响应式的。
- 数组的响应式
- 对响应式数组进行添加、删除、修改等操作都会触发依赖这个数组的组件重新渲染。
<template>
<div>
<ul>
<li v-for="item in personArr" :key="item.id">
姓名:{{ item.name }} , 年龄:{{ item.age }} 岁
</li>
</ul>
<button @click="addPerson">添加</button>
</div>
</template>
<script setup lang="ts">
import { reactive } from 'vue';
const personArr = reactive([
{id: 1, name: '张三', age: 17 },
{id: 2, name: '李四', age: 18 },
{id: 3, name: '王二', age: 16 },
]);
console.log(personArr);
const addPerson = () => {
personArr.push({ id: 4, name: '张麻子', age: 16 });
};
</script>
控制台打印:

- 直接赋值整个响应式对象不会触发响应式更新
let person = reactive({
name: '张三',
age: 36,
});
// 这样不会触发响应式更新
const changePerson = () => {
person = {
name: 'john',
age: 30
}
}
// 使用Object.assign()等方法来更新属性
const changePerson = () => {
Object.assign(person, {
name: '李四',
age: 24,
});
}
- 响应式对象的属性必须在创建时存在
const person = reactive({
name: '王二',
});
// 报错:类型“{ name: string; }”上不存在属性“age”。
person.age = 30;
ref()
基本概念
ref()接受一个内部值,返回一个响应式的、可更改的 ref 对象,此对象只有一个指向其内部值的属性 .value。
- 作用:定义响应式的变量。
- 可接收的数据类型:基本类型、对象类型的响应式数据。若
ref接收的是对象类型,内部其实也是调用了reactive函数。 - 语法:
let xxx = ref(初始值)。 - 返回值:一个
RefImpl的实例对象,简称ref对象或ref,ref对象的value属性是响应式的。 - 类型:
function ref<T>(value: T): Ref<UnwrapRef<T>>
interface Ref<T> {
value: T
}
ref 对象是可更改的,也就是说可以为 .value 赋予新的值。它也是响应式的,即所有对 .value 的操作都将被追踪。
使用 ref() 创建基本类型的响应式数据
<template>
<div>
<p>count: {{ count }}</p>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
// 在组合式 API 中,推荐使用 ref() 函数来声明响应式状态
const count = ref(0)
consoe.log(count) // count 是一个RefImpl的实例对象
console.log('count.value:', count.value) // count.value:0
count.value = 1
console.log('count.value:', count.value) // count.value:1
</script>
控制台打印:

注意:
JS中操作数据需要:xxx.value,但模板中不需要.value,直接使用{{xxx}}。- 对于
const count = ref(0)来说,count不是响应式的,count.value是响应式的。
ref()深层响应性
当ref()包裹的是一个对象时,对这个对象的属性进行修改也会触发响应式更新。
<template>
<div>
<p>姓名: {{ person.name }}</p>
<p>年龄: {{ person.age }}</p>
<button @click="changePerson">修改信息</button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const person = ref({
name: '张三',
age: 36
})
console.log(person)
const changePerson = () => {
person.name = '李四'
person.age = 24
}
</script>
使用ref()定义一个对象类型的响应式数据,页面是正常展示数据的:

数据详细信息解析:
RefImpl {... }:这表示这是一个由ref函数创建的响应式对象的内部实现结构展示。__v_isShallow:表示是否是浅层响应式,这里为false,说明不是浅层响应式。dep:这是一个依赖收集器,用于跟踪哪些部分的代码依赖于这个响应式对象。当响应式对象的值发生变化时,会通知依赖它的部分进行更新。这里显示为一个包含一个ReactiveEffect的Map,说明有一个依赖项。__v_isRef:为true,表明这是一个由ref创建的响应式引用。_rawValue:存储了原始的值,这里是一个包含name和age属性的对象。_value和value:都是代理对象,通过代理可以实现对原始对象的响应式追踪。[[Handler]]和[[Target]]:是与代理对象相关的内部属性,[[Handler]]是处理程序,用于定义对目标对象的各种操作的拦截行为,[[Target]]是被代理的原始对象。
从控制台打印的数据结构可以看出:如果将一个对象赋值给 ref,那么这个对象将通过 reactive() 转为具有深层次响应式的对象。
表面上它返回来的是一个RefImpl的实例对象,但是在这个实例对象的_value、value属性里,是 reactive() 的返回值:一个Proxy的实例对象。
直接调用changePerson修改信息,person没有被修改。编译器会报错:
const changePerson = () => {
// 报错:类型“Ref<{ name: string; age: number; }>”上不存在属性“name”。
person.name = '李四'
// 报错:类型“Ref<{ name: string; age: number; }>”上不存在属性“age”。
person.age = 24
console.log(person)
}
ref定义的数据,如果要修改,要用.value来更改。
const changePerson = () => {
console.log('修改前打印person.value:', person.value)
person.value.name = '李四'
person.value.age = 24
console.log(person)
}

可以使用Vue - Official插件自动添加.value。


ref() 与 reactive()的区别
- 从数据类型看:
ref()用来定义:基本类型数据、对象类型数据;reactive()用来定义:对象类型数据。
- 返回值类型:
ref()返回值: 一个RefImpl的实例对象。reactive()返回值:一个Proxy的实例对象,简称:响应式对象。
- 响应式更新方式:
ref()通过修改.value属性来触发响应式更新。
ref()跳过.value属性直接修改变量的值,不会出发响应式更新。
import { ref } from 'vue';
let person = ref({name: '张三', age: 36});
const changePerson = () => {
// 重新分配一个对象,触发响应式更新
person.value = { name: '李四', age: 24 }
// ref 跳过 .value,不会出发响应式更新
person = { name: '李四', age: 24 }
}
let count = ref(0)
const changeCount = () => {
// 可以触发响应式更新
count.value ++;
// 不会触发响应式更新
count = ref(10)
}
reactive()直接修改对象的属性即可触发响应式更新。
import { reactive } from 'vue';
let person = reactive({name: '张三', age: 36});
const changePerson = () => {
person.name = '李四' // 触发响应式更新
person.age = 30 // 触发响应式更新
}
- reactive()重新分配一个新对象,会失去响应式(可以使用Object.assign去整体替换)。
import { reactive } from 'vue';
let person = reactive({name: '张三', age: 36});
// 不会触发响应式更新
const changePerson = () => {
// 重新分配一个对象
person = { name: '李四', age: 24 }
// person = reactive({name: '张三', age: 36});
// 与person = reactive({ name: '李四',age: 24 });
// 是完全不同的2个完全不同的person, 根本不是同一个东西
person = reactive({ name: '李四', age: 24 });
}
// 使用Object.assign()等方法来更新属性
const changePerson = () => {
Object.assign(person, {
name: '李四',
age: 24,
});
}
-
深层响应性:
reactive()可以自动保持对象的深层响应性,即嵌套对象的属性修改也会触发响应式更新。- 对于
ref()包裹的对象,直接修改嵌套对象的属性可能不会触发响应式更新,需要特殊处理。
-
使用场景:
- 若需要一个基本类型的响应式数据,必须使用
ref()。 - 若需要一个响应式对象,层级不深,
ref()、reactive()都可以。 - 若需要一个响应式对象,且层级较深,推荐使用
reactive()。
- 若需要一个基本类型的响应式数据,必须使用










