响应式 API
什么是响应式 API?
Vue3 提供了一系列响应式 API,用于创建和管理响应式数据。这些 API 是组合式 API 的核心,包括 ref、reactive、computed、watch 等。
1. ref() - 基本类型响应式
ref() 函数用于创建一个响应式的引用,主要用于基本数据类型(Number、String、Boolean 等)。
示例:使用 ref()
<script setup>
import { ref } from 'vue';
// 创建响应式数据
const count = ref(0);
const message = ref('Hello');
const isActive = ref(false);
// 访问和修改响应式数据
console.log(count.value); // 0
count.value++; // 修改数据
console.log(count.value); // 1
// ref 也可以用于对象和数组
const user = ref({ name: 'John', age: 30 });
user.value.name = 'Jane'; // 可以直接修改属性
</script>
2. reactive() - 对象类型响应式
reactive() 函数用于创建一个响应式对象,主要用于复杂数据类型(Object、Array 等)。
示例:使用 reactive()
<script setup>
import { reactive } from 'vue';
// 创建响应式对象
const state = reactive({
count: 0,
message: 'Hello',
user: {
name: 'John',
age: 30
},
items: ['apple', 'banana', 'orange']
});
// 访问和修改响应式数据
console.log(state.count); // 0
state.count++; // 修改数据
console.log(state.count); // 1
// 修改嵌套属性
state.user.name = 'Jane';
// 修改数组
state.items.push('grape');
</script>
3. computed() - 计算属性
computed() 函数用于创建一个计算属性,它会基于依赖自动缓存结果。
示例:使用 computed()
<script setup>
import { ref, computed } from 'vue';
const count = ref(0);
// 只读计算属性
const doubleCount = computed(() => count.value * 2);
console.log(doubleCount.value); // 0
count.value++;
console.log(doubleCount.value); // 2
// 可写计算属性
const fullName = computed({
get: () => `${firstName.value} ${lastName.value}`,
set: (newValue) => {
const [first, last] = newValue.split(' ');
firstName.value = first;
lastName.value = last;
}
});
</script>
4. watch() - 监听器
watch() 函数用于监听响应式数据的变化,并执行相应的副作用。
示例:使用 watch()
<script setup>
import { ref, watch } from 'vue';
const count = ref(0);
const message = ref('Hello');
// 监听单个源
watch(count, (newValue, oldValue) => {
console.log(`count 从 ${oldValue} 变为 ${newValue}`);
});
// 监听多个源
watch([count, message], ([newCount, newMessage], [oldCount, oldMessage]) => {
console.log(`count: ${oldCount} -> ${newCount}`);
console.log(`message: ${oldMessage} -> ${newMessage}`);
});
// 监听对象的深层变化
const user = ref({ name: 'John', age: 30 });
watch(user, (newUser, oldUser) => {
console.log('user 发生变化');
}, { deep: true });
// 立即执行
watch(count, (newValue) => {
console.log(`count: ${newValue}`);
}, { immediate: true });
</script>
5. watchEffect() - 自动追踪依赖
watchEffect() 函数用于创建一个副作用,它会自动追踪依赖,当依赖变化时重新执行。
示例:使用 watchEffect()
<script setup>
import { ref, watchEffect } from 'vue';
const count = ref(0);
const message = ref('Hello');
// 自动追踪依赖
const stop = watchEffect(() => {
console.log(`count: ${count.value}, message: ${message.value}`);
});
// 修改依赖
count.value++;
message.value = 'World';
// 停止监听
// stop();
</script>
6. toRef() - 从响应式对象创建 ref
toRef() 函数用于从响应式对象中提取一个属性,并创建一个对应的 ref。
示例:使用 toRef()
<script setup>
import { reactive, toRef } from 'vue';
const state = reactive({ name: 'John', age: 30 });
// 从响应式对象创建 ref
const nameRef = toRef(state, 'name');
const ageRef = toRef(state, 'age');
// 修改 ref 会影响原对象
nameRef.value = 'Jane';
console.log(state.name); // Jane
// 修改原对象会影响 ref
state.age = 25;
console.log(ageRef.value); // 25
</script>
7. toRefs() - 将响应式对象转换为 ref 对象
toRefs() 函数用于将一个响应式对象转换为一个普通对象,其中每个属性都是一个 ref。
示例:使用 toRefs()
<script setup>
import { reactive, toRefs } from 'vue';
const state = reactive({ name: 'John', age: 30 });
// 将响应式对象转换为 ref 对象
const refs = toRefs(state);
// 可以安全解构
const { name, age } = refs;
console.log(name.value); // John
console.log(age.value); // 30
// 修改 ref 会影响原对象
name.value = 'Jane';
console.log(state.name); // Jane
</script>
8. unref() - 解包 ref
unref() 函数用于解包 ref 对象,如果参数是 ref,则返回其 value,否则返回参数本身。
示例:使用 unref()
<script setup>
import { ref, unref } from 'vue';
const count = ref(0);
const message = 'Hello';
console.log(unref(count)); // 0 (解包 ref)
console.log(unref(message)); // Hello (直接返回)
// 等价于
// val = isRef(val) ? val.value : val;
</script>
9. shallowRef() 和 shallowReactive() - 浅层响应式
shallowRef() 和 shallowReactive() 函数用于创建浅层响应式数据,只响应顶层属性的变化。
示例:使用 shallowRef() 和 shallowReactive()
<script setup>
import { shallowRef, shallowReactive } from 'vue';
// shallowRef 只响应 .value 的变化
const shallowCount = shallowRef({ count: 0 });
shallowCount.value.count++;
// 不会触发更新,因为只监听 .value
shallowCount.value = { count: 1 }; // 会触发更新
// shallowReactive 只响应顶层属性的变化
const shallowState = shallowReactive({
count: 0,
user: { name: 'John', age: 30 }
});
shallowState.count++;
// 会触发更新,因为 count 是顶层属性
shallowState.user.name = 'Jane';
// 不会触发更新,因为 user.name 是嵌套属性
</script>
10. readonly() - 只读响应式
readonly() 函数用于创建一个只读的响应式对象,防止修改其属性。
示例:使用 readonly()
<script setup>
import { reactive, readonly } from 'vue';
const state = reactive({ count: 0 });
const readonlyState = readonly(state);
// 尝试修改只读对象
readonlyState.count++;
// 警告:Cannot assign to 'count' because it is a read-only property
// 修改原对象会影响只读对象
state.count++;
console.log(readonlyState.count); // 1
</script>
响应式 API 比较
| API | 用途 | 适用类型 | 特点 |
|---|---|---|---|
| ref() | 创建响应式引用 | 所有类型 | 需要通过 .value 访问和修改 |
| reactive() | 创建响应式对象 | 对象和数组 | 直接访问和修改属性 |
| computed() | 创建计算属性 | 所有类型 | 缓存结果,依赖变化时更新 |
| watch() | 监听数据变化 | 所有类型 | 可以监听多个源,配置选项丰富 |
| watchEffect() | 自动追踪依赖 | 所有类型 | 自动追踪依赖,简洁易用 |
最佳实践
- 对于基本类型,优先使用 ref()
- 对于对象和数组,优先使用 reactive()
- 需要解构响应式对象时,使用 toRefs()
- 计算属性使用 computed()
- 简单监听使用 watchEffect(),复杂监听使用 watch()
- 避免在模板中使用 .value,Vue 会自动解包