响应式对象
约 832 字大约 3 分钟
2025-08-05
ref()
响应式引用
ref()
一般用来创建对任意值(基本类型、对象)的响应式引用,它的返回值是一个实例类型为 RefImpl
的实例对象,简称 ref 对象。其实 ref()
就是对 reactive()
的包装。
在 JavaScript 中需要通过 .value
来访问实际值。但模板代码中不需要 .value
,直接使用即可。模板省略 .value
的操作在 Vue3 中叫做 自动解包。
import { ref } from "vue";
// 基本类型
const count = ref(0);
// 引用类型
const user = ref({ name: "John", age: 30 });
// 替换值
count.value = 12;
user.value.name = "libao";
// 对象的完全替换
user.value = { name: "zhangsan", age: 40 };
// ts 写法
type User = { name: string; age: number };
const lisi = reactive<User>({ name: "lisi", age: 11 });
const laowang: User = reactive({ name: "laowang", age: 11 });
console.log(lisi);
为什么需要通过 .value
来访问实际值
因为 Vue 是通过对象的 geeter
、setter
方法来拦截对象属性的 get、set 操作,从而实现对属性的依赖追踪。
因为基本数据类型是没有属性的,所以需要把基本类型套层壳,包装成对象再处理。
const demoRef = {
_value: 0,
get value() {
track(); // 收集依赖,组件订阅
return this._value;
},
set value(newValue) {
this._value = newValue;
trigger(); // 触发更新,订阅通知
},
};
演示案例
<template>
<div class="person">
<div class="left">
<div>
<span>计数器:</span>
<span>{{ count }}</span>
</div>
<div>
<span>深层对象obj:</span>
<span>{{ obj.a.b.c }}</span>
</div>
<div>
<span>用户:</span>
<span>{{ user.name }}</span>
</div>
<div>
<span>年龄:</span>
<span>{{ user.age }}</span>
</div>
</div>
<div class="right">
<button class="demo-btn m_5" @click="changeCount">计数器 + 1</button>
<button class="demo-btn m_5" @click="changeObj">obj.a.b.c</button>
<button class="demo-btn m_5" @click="changeName">修改名字</button>
<button class="demo-btn m_5" @click="changeAge">年龄 + 1</button>
<button class="demo-btn m_5" @click="changeUser">替换用户</button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, defineOptions } from "vue";
defineOptions({
name: "Person123",
});
// 基本类型
const count = ref(0);
// 引用类型
const user = ref({ name: "John", age: 30 });
const obj = ref({ a: { b: { c: 1 } } });
// 替换值
changeObj;
function changeCount() {
count.value += 1;
}
function changeObj() {
obj.value.a.b.c += 1;
}
function changeName() {
user.value.name += "6";
}
function changeAge() {
user.value.age += 1;
}
function changeUser() {
user.value = { name: "zhangsan", age: 40 };
}
</script>
<style lang="scss" scoped>
.person {
display: flex;
flex-direction: row;
}
.left {
flex: 3;
margin-right: 20px;
font-size: 16px;
div {
display: flex;
margin-bottom: 10px;
span {
flex: 1;
display: flex;
align-items: center;
word-break: break-all;
}
}
}
.right {
flex: 2;
button {
margin: 5px 10px;
}
}
</style>
reactive()
响应式对象
创建深度响应式的对象的代理(仅适用于对象、数组、Map、Set 等集合类型)。它的返回值是一个 Proxy
代理的实例对象。
import { reactive } from "vue";
// 对象
const state = reactive({
count: 0,
user: { name: "John" },
});
console.log(state.count);
// 数组
const list = reactive([1, 2, 3]);
我们不能直接整个修改 reactive
包装对象的值
list = { name: "1" };
可以借助 Object.asign()
来拷贝对象
Object.asign(list, { name: "1" });
二者对比
区别
特性 | ref | reactive |
---|---|---|
支持数据类型 | 所有类型(基本类型 + 对象) | 仅对象类型(对象/数组/Map/Set) |
访问方式 | 通过 .value 访问 | 直接访问属性 |
重新赋值 | 支持完整替换(ref.value = {} ) | 无法直接完整替换 |
响应式深度 | 对象类型会使用 reactive | 深层响应式(惰性代理) |
TS 类型支持 | 需显式泛型:ref<Type>() | 自动类型推断 |
解构响应性 | 单个值保持响应性 | 需 toRefs 包装来保持响应性 |
性能特点 | 简单值内存开销小 | 大型对象性能更优 |
实践建议
- 若需要一个基本类型的响应式数据,必须使用
ref
。 - 若需要一个响应式对象,层级不深,
ref
、reactive
都可以。 - 若需要一个响应式对象,且层级较深,推荐使用
reactive
。
toRefs()
、toRef()
二者的功能基本一致:将一个响应式对象中的每一个属性,转换为一个新的 ref
对象,同时保持原有的依赖和订阅。
区别在于:
toReefs
可以批量转换所有属性toRef
只能转换指定属性
import { ref, reactive, toRefs, toRef } from "vue";
// 原始数据
let person = reactive({ name: "张三", age: 18, gender: "男" });
// 通过toRefs将person对象中的n个属性批量取出,且依然保持响应式的能力
let pesonRef = toRefs(person);
let { name, gender } = toRefs(person);
// 通过toRef将person对象中的gender属性取出,且依然保持响应式的能力
let age = toRef(person, "age");
演示案例
<template>
<div class="person">
<div>姓名:{{ person.name }}</div>
<div>年龄:{{ person.age }}</div>
<div>性别:{{ person.gender }}</div>
<br />
<button class="demo-btn m_5" @click="changeName">修改名字</button>
<button class="demo-btn m_5" @click="changeAge">修改年龄</button>
<button class="demo-btn m_5" @click="changeGender">修改性别</button>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, toRefs, toRef, defineOptions } from "vue";
defineOptions({
name: "Person",
});
// 数据
let person = reactive({ name: "张三", age: 18, gender: "男" });
let person1 = toRefs(person);
// 通过toRefs将person对象中的n个属性批量取出,且依然保持响应式的能力
let { name, gender } = toRefs(person);
// 通过toRef将person对象中的gender属性取出,且依然保持响应式的能力
let age = toRef(person, "age");
// 方法
function changeName() {
name.value += "~";
}
function changeAge() {
age.value += 1;
}
function changeGender() {
gender.value = "女";
}
</script>
更新日志
2025/8/24 08:17
查看所有更新日志
e7112
-1于