内部实现与行为
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 包装,尤其是深层嵌套对象,会有较大的性能开销,但其管理复杂状态的能力强大。
实际开发中的最佳实践
- 组合使用:在实际开发中,经常需要组合使用
ref和reactive。例如,使用reactive管理复杂对象的整体状态,使用ref管理简单值或临时变量。
import { ref, reactive } from 'vue';
const state = reactive({
count: 0,
user: { name: 'Alice', age: 25 }
});
const isLoggedIn = ref(false);
- 深层响应式对象的解构:当需要单独使用深层响应式对象的某个属性时,可以使用
toRefs将reactive对象转换为多个ref。
import { reactive, toRefs } from 'vue';
const state = reactive({ a: 1, b: 2 });
const { a, b } = toRefs(state);
总结
ref:用于创建单一值的响应式引用,适合基本类型数据或非响应式对象的包装。需要通过.value属性访问和修改值。reactive:用于创建复杂对象的深度响应式代理,适合管理包含多个属性和嵌套结构的状态。直接访问和修改对象属性。