各位靓仔靓女,准备好上车了吗?今天咱们来聊聊 Vue 3 里面的两位当家花旦:ref
和 reactive
。
大家好啊!今天咱们不搞虚的,直接上干货,一起扒一扒 Vue 3 里 ref
和 reactive
这俩“姐妹花”的底裤,看看她们到底有啥区别,又各自有啥优缺点。保证让你听完之后,以后再用她们,就像老司机开车一样,稳得一批!
开胃小菜:响应式是个啥?
在深入了解 ref
和 reactive
之前,咱们先简单回顾一下 Vue 的核心概念:响应式。
所谓响应式,就是当你修改了数据,视图(也就是你看到的网页)会自动更新。这背后,Vue 默默地做了很多工作,它会“监听”你的数据,一旦发现数据变化,就通知视图进行更新。
这就好比你养了一只宠物,你给它喂食,它就长大。喂食(修改数据)就是你的操作,长大(视图更新)就是宠物的反应。Vue 就是那个帮你看着宠物,一旦你喂食,就让宠物长大的“保姆”。
正餐来了:ref
和 reactive
的本质区别
现在,咱们开始进入正题,看看 ref
和 reactive
到底有啥不同。
1. 作用对象不同
ref
: 主要用于包装单个基本类型值 (string, number, boolean) 或单个对象。它可以让你创建一个响应式的变量,而不是一个响应式的对象。reactive
: 主要用于创建对象的响应式副本。它会递归地将一个对象的所有属性都变成响应式的。
简单来说,ref
更像是一个“引用”,它指向一个值,而 reactive
则是直接让一个对象变得“有感觉”。
代码示例:
import { ref, reactive } from 'vue';
// 使用 ref
const count = ref(0); // count 是一个 RefImpl 对象,其 value 属性才是 0
// 使用 reactive
const person = reactive({
name: '张三',
age: 18
});
2. 访问方式不同
ref
: 需要通过.value
属性来访问或修改其值。reactive
: 直接访问和修改属性即可。
这就像你跟朋友借钱,ref
就像你借的是现金,你需要打开钱包(.value
)才能拿到钱;reactive
就像你借的是银行卡,直接刷卡就行。
代码示例:
import { ref, reactive } from 'vue';
const count = ref(0);
const person = reactive({
name: '张三',
age: 18
});
console.log(count.value); // 输出 0
count.value++; // 修改 count 的值
console.log(person.name); // 输出 "张三"
person.age = 20; // 修改 person 的 age 属性
3. 底层实现不同
ref
: 底层使用了RefImpl
类,它是一个简单的对象,只有一个value
属性,通过getter
和setter
来实现响应式。reactive
: 底层使用了Proxy
,它会拦截对对象属性的访问和修改,从而实现响应式。
Proxy
比 RefImpl
更加强大,它可以监听更多的操作,比如属性的添加和删除。
4. 解包行为不同
当 ref
在模板中使用时,会自动解包,可以直接访问其值,而不需要 .value
。
代码示例:
<template>
<div>
<p>Count: {{ count }}</p> <!-- 不需要 count.value -->
<p>Name: {{ person.name }}</p>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue';
const count = ref(0);
const person = reactive({
name: '张三',
age: 18
});
</script>
深度剖析:内存占用和性能对比
现在,咱们来聊聊大家最关心的问题:ref
和 reactive
在内存占用和性能上,到底哪个更胜一筹?
1. 内存占用
ref
: 内存占用相对较小。因为它只是包装了一个值,只需要存储一个RefImpl
对象和一个值即可。reactive
: 内存占用相对较大。因为它需要创建一个Proxy
对象,并递归地将对象的所有属性都变成响应式的。
如果你的数据结构非常复杂,包含大量的属性,那么使用 reactive
可能会占用更多的内存。
2. 性能
ref
: 性能相对较好。因为它只需要监听value
属性的变化,开销较小。reactive
: 性能相对较差。因为它需要拦截对对象属性的所有访问和修改,开销较大。
特别是在频繁修改对象属性的情况下,reactive
的性能可能会受到影响。
表格总结:
特性 | ref |
reactive |
---|---|---|
作用对象 | 单个基本类型值或单个对象 | 对象 |
访问方式 | 通过 .value |
直接访问 |
底层实现 | RefImpl |
Proxy |
内存占用 | 较小 | 较大 |
性能 | 较好 | 相对较差 |
使用场景 | 简单数据,需要单独追踪的数据 | 复杂对象,需要整体响应式的数据 |
解包行为 | 在模板中自动解包 | 无解包行为 |
案例分析:选择困难症怎么办?
知道了 ref
和 reactive
的区别,那在实际开发中,到底该怎么选择呢?别慌,咱们来几个案例分析,帮你解决选择困难症。
案例一:计数器
<template>
<div>
<button @click="increment">Increment</button>
<p>Count: {{ count }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue';
const count = ref(0);
function increment() {
count.value++;
}
</script>
在这个例子中,我们只需要追踪一个简单的计数器,使用 ref
就足够了。
案例二:用户信息
<template>
<div>
<p>Name: {{ user.name }}</p>
<p>Age: {{ user.age }}</p>
<button @click="updateAge">Update Age</button>
</div>
</template>
<script setup>
import { reactive } from 'vue';
const user = reactive({
name: '李四',
age: 25
});
function updateAge() {
user.age++;
}
</script>
在这个例子中,我们需要追踪一个包含多个属性的用户对象,使用 reactive
可以更方便地实现响应式。
案例三:表单数据
<template>
<form @submit.prevent="handleSubmit">
<input type="text" v-model="formData.name">
<input type="email" v-model="formData.email">
<button type="submit">Submit</button>
</form>
</template>
<script setup>
import { reactive } from 'vue';
const formData = reactive({
name: '',
email: ''
});
function handleSubmit() {
console.log(formData);
}
</script>
对于表单数据,通常包含多个字段,使用 reactive
可以更方便地管理这些数据。
总结:
- 如果你的数据很简单,只需要追踪单个值,那就用
ref
。 - 如果你的数据是一个复杂的对象,包含多个属性,那就用
reactive
。 - 如果你对性能有极致的要求,并且只需要追踪对象的部分属性,可以考虑使用
ref
来包装这些属性。
高级技巧:toRefs
和 toRef
Vue 3 还提供了 toRefs
和 toRef
这两个 API,它们可以将 reactive
对象中的属性转换为 ref
。
toRefs
: 将 reactive
对象的所有属性都转换为 ref
。
代码示例:
import { reactive, toRefs } from 'vue';
const person = reactive({
name: '王五',
age: 30
});
const { name, age } = toRefs(person);
console.log(name.value); // 输出 "王五"
console.log(age.value); // 输出 30
person.name = '赵六';
console.log(name.value); // 输出 "赵六"
toRef
: 将 reactive
对象的单个属性转换为 ref
。
代码示例:
import { reactive, toRef } from 'vue';
const person = reactive({
name: '田七',
age: 35
});
const nameRef = toRef(person, 'name');
console.log(nameRef.value); // 输出 "田七"
person.name = '周八';
console.log(nameRef.value); // 输出 "周八"
为什么要使用 toRefs
和 toRef
?
- 解耦: 可以将
reactive
对象中的属性解耦出来,方便单独使用和管理。 - 提高性能: 如果只需要追踪对象的少数几个属性,可以使用
toRef
将这些属性转换为ref
,从而减少Proxy
的开销。 - 组合式 API: 在组合式 API 中,
toRefs
可以方便地将reactive
对象中的属性暴露给外部使用。
注意事项:shallowReactive
和 shallowRef
Vue 3 还提供了 shallowReactive
和 shallowRef
这两个 API,它们可以创建浅层的响应式对象和 ref
。
shallowReactive
: 只会将对象的第一层属性变成响应式的,不会递归地转换嵌套对象。
shallowRef
: 只会追踪 value
属性的变化,不会追踪 value
属性内部的变化。
代码示例:
import { reactive, shallowReactive } from 'vue';
const person = reactive({
name: '钱九',
address: {
city: '北京'
}
});
const shallowPerson = shallowReactive({
name: '孙十',
address: {
city: '上海'
}
});
person.address.city = '深圳'; // 会触发视图更新
shallowPerson.address.city = '广州'; // 不会触发视图更新
什么情况下使用 shallowReactive
和 shallowRef
?
- 性能优化: 如果你的对象结构非常复杂,并且只需要追踪第一层属性的变化,可以使用
shallowReactive
来提高性能。 - 外部数据: 如果你的数据来自外部,并且不需要 Vue 进行深度追踪,可以使用
shallowRef
来包装这些数据。
总结:选择适合你的“舞伴”
总而言之,ref
和 reactive
都是 Vue 3 中非常重要的 API,它们各有优缺点,适用于不同的场景。
选择哪个 API,取决于你的具体需求。就像跳舞一样,你需要选择一个适合你的“舞伴”,才能跳出最美的舞姿。
希望今天的讲解能够帮助你更好地理解 ref
和 reactive
,让你在 Vue 3 的世界里,玩得更溜!
各位,下课!