文章目录
在 Vue 3 中,ref()
和 reactive()
都是用于响应式数据管理的 API,它们的主要作用是让数据具有响应性,使 Vue 组件在数据变化时能够自动更新视图。它们有一定的关联,但用法和适用场景有所不同。
1. ref()
和 reactive()
的区别
对比点 | ref() |
reactive() |
---|---|---|
适用场景 | 适用于基本类型(Number、String、Boolean)和对象 | 适用于对象(Object、Array、Map、Set等) |
是否需要 .value |
需要:count.value |
不需要,直接访问属性:state.count |
是否可以解构 | 不能直接解构,会丢失响应性 | 不能直接解构,会丢失响应性 |
适用于数组/对象 | 适用于任意类型的数据,但访问对象时需要 .value |
适用于复杂的对象和数组 |
底层实现 | ref 内部使用 reactive 处理对象 |
使用 Proxy 代理对象,实现响应性 |
2. 解构
详解
情况 | 示例 | 是否丢失响应性? | 原因 | 解决方案 |
---|---|---|---|---|
ref(基本类型) |
const count = ref(0); count.value++ |
✅ 不会 | count 本身是 ref ,Vue 追踪 count.value |
直接 count.value++ |
ref(对象类型) 并直接修改 .value |
const user = ref({ name: 'Alice' }); user.value.name = 'Bob'; |
✅ 不会 | user.value 仍然是响应式对象 |
直接修改 user.value.name |
解构 ref(对象).value |
const { name } = user.value; name = 'Bob'; |
❌ 会丢失 | name 是普通变量,Vue 不再追踪 |
使用 toRefs(user.value) |
解构 reactive(对象) |
const user = reactive({ name: 'Alice' }); const { name } = user; name = 'Bob'; |
✅ 不会 | reactive 生成的是 Proxy ,解构后仍然保持响应性 |
直接解构 const { name } = user; |
reactive 对象包含 ref() |
const user = reactive({ name: ref('Alice') }); user.name = 'Bob'; |
✅ 不会 | user.name 是 ref ,Vue 能追踪 user.name.value |
访问 user.name.value |
reactive 里存 ref 并解构 |
const user = reactive({ name: ref('Alice') }); const { name } = user; name = 'Bob'; |
❌ 会丢失 | name 变成 ref ,但解构后 Vue 不会追踪 name.value |
改为 toRef(user, 'name') |
解构 reactive() 并使用 toRefs() |
const user = reactive({ name: 'Alice' }); const { name } = toRefs(user); |
✅ 不会 | toRefs() 把 user.name 转换为 ref ,仍然响应式 |
继续 name.value = 'Bob' |
2.1. 什么是解构
解构是 JavaScript用于从对象或数组中提取值的语法,允许你将数据拆分并存储到独立的变量中。
看个例子,一眼便知
<script setup>
import { reactive } from 'vue';
const state = reactive({
count: 0,
message: 'Hello'
});
// ❌ 直接解构(会丢失响应性)
let { count, message } = state;
const increment = () => {
count++; // ❌ 这里不会触发视图更新
};
</script>
<template>
<p>{{ count }}</p> <!-- 不会更新 -->
<button @click="increment">+</button>
</template>
2.2. 解构避免丢失响应性的办法
在 Vue 3 中,reactive()
返回的对象在解构后会丢失响应性,这是因为 reactive()
使用的是 Proxy
,而解构时只会拷贝属性值,而不会保留 Proxy
代理能力。
2.2.1. 解决方案:toRefs()
保持响应性
toRefs(state)
把state
的每个属性变成ref()
,这样解构出来的count
和message
就是ref
,修改.value
仍然会触发 Vue 的响应式系统。
toRefs()
用于将 reactive
对象的属性转换成 ref
,这样解构后仍然保持响应性:
<script setup>
import { reactive, toRefs } from 'vue';
const state = reactive({
count: 0,
message: 'Hello'
});
// ✅ 使用 toRefs() 让解构后的变量仍保持响应性
const { count, message } = toRefs(state);
const increment = () => {
count.value++; // ✅ 这里会触发视图更新
};
</script>
<template>
<p>{{ count }}</p> <!-- 视图会更新 -->
<button @click="increment">+</button>
</template>
2.2.2. 解决方案: toRef()
保持响应性
如果你只想让某个属性保持响应性,而不是整个 state
,可以使用 toRef()
:
<script setup>
import { reactive, toRef } from 'vue';
const state = reactive({
count: 0,
message: 'Hello'
});
// ✅ 只把 count 变成 ref
const count = toRef(state, 'count');
const increment = () => {
count.value++; // ✅ 仍然是响应式的
};
</script>
<template>
<p>{{ count }}</p>
<button @click="increment">+</button>
</template>
3. 最佳实践
- 基本类型 ➝
ref()
- 对象 ➝
reactive()
- 解构
reactive
➝ 用toRefs()
- 解构
ref(对象).value
➝ 不能直接解构,必须用toRefs()