响应式 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 会自动解包