一文搞懂vue3的 ref 和 reactive

417 阅读3分钟

内部实现与行为

ref 的实现与行为

ref 是通过 RefImpl 类来实现的,它是一个对单一值的响应式包装。具体来说:

  • ref 创建一个包含 .value 属性的对象,这个属性持有实际的值。
  • .value 属性被访问或修改时,Vue 的响应式系统会追踪依赖,并在值变化时通知这些依赖进行更新。

示例:

import { ref } from 'vue';

const count = ref(0);

console.log(count.value); // 0

count.value = 1; // 修改 .value,触发依赖更新
console.log(count.value); // 1

reactive 的实现与行为

reactive 是通过 Proxy 对象来实现的,它为整个对象提供响应式代理。具体来说:

  • reactive 将一个对象包装在 Proxy 内部,拦截对象的所有操作(如获取、设置属性等),并自动管理响应式依赖。
  • reactive 对象的所有嵌套属性也是响应式的,即使是深层次的嵌套。

示例:

import { reactive } from 'vue';

const state = reactive({
  count: 0,
  user: {
    name: 'Alice',
    age: 25
  }
});

console.log(state.count); // 0

state.count = 1; // 修改属性,触发依赖更新
console.log(state.count); // 1

console.log(state.user.name); // Alice

state.user.age = 26; // 修改嵌套属性,触发依赖更新
console.log(state.user.age); // 26

深度响应与浅层响应

  • ref:对单一值是浅层响应式的,对于对象需要手动展开 .value 属性才能实现深层响应。例如,ref 包含一个对象时,必须通过 toRefs 或者将其解构成多个 ref
const obj = ref({ a: 1, b: 2 });
obj.value.a = 3; // 触发响应
  • reactive:提供深层响应式,即使是嵌套的对象和数组也是响应式的。
const state = reactive({ a: 1, b: { c: 2 } });
state.b.c = 3; // 触发响应

场景与用途

使用场景

  • 基本类型数据ref 更适合处理单一的基本类型数据,比如数值、字符串、布尔值等。

  • 复杂结构数据reactive 更适合处理复杂的嵌套对象和数组,因为它可以自动处理嵌套属性的响应式。

用法场景

  • 表单输入绑定ref 经常用于表单输入的双向绑定,因为它处理单一的基本类型数据非常方便。
const inputValue = ref('');
  • 组件状态管理reactive 通常用于管理组件的整体状态,特别是当状态包含多个属性和嵌套对象时。
const state = reactive({
  count: 0,
  user: { name: 'John', age: 30 }
});

性能考虑

  • ref 性能开销较小:由于 ref 只需追踪单一值的变化,性能开销较小,适合频繁更新的简单值。

  • reactive 性能开销较大reactive 需要对整个对象进行 Proxy 包装,尤其是深层嵌套对象,会有较大的性能开销,但其管理复杂状态的能力强大。

实际开发中的最佳实践

  • 组合使用:在实际开发中,经常需要组合使用 refreactive。例如,使用 reactive 管理复杂对象的整体状态,使用 ref 管理简单值或临时变量。
import { ref, reactive } from 'vue';

const state = reactive({
  count: 0,
  user: { name: 'Alice', age: 25 }
});

const isLoggedIn = ref(false);
  • 深层响应式对象的解构:当需要单独使用深层响应式对象的某个属性时,可以使用 toRefsreactive 对象转换为多个 ref
import { reactive, toRefs } from 'vue';

const state = reactive({ a: 1, b: 2 });
const { a, b } = toRefs(state);

总结

  • ref:用于创建单一值的响应式引用,适合基本类型数据或非响应式对象的包装。需要通过 .value 属性访问和修改值。
  • reactive:用于创建复杂对象的深度响应式代理,适合管理包含多个属性和嵌套结构的状态。直接访问和修改对象属性。