深入理解为什么需要 ref、toRef、toRefs
面试速答(30 秒版 TL;DR)
ref:把一个值(尤其是基本类型)包装成响应式引用,读写用.value。reactive:把对象变成响应式代理(Proxy)。toRef:把一个响应式对象上的某个属性“映射成 ref”,保持与源属性联动。toRefs:把响应式对象的每个属性都转成 ref,常用于 解构后仍保持响应式。- 为什么需要:因为直接解构
reactive会丢失响应式关联,toRef(s)用来“保住响应式链接”。
先把核心坑说清楚:解构会断开响应式
import { reactive, watchEffect } from 'vue';
const state = reactive({ count: 0 });
const { count } = state; // 这里 count 是普通 number,不是响应式
watchEffect(() => {
// 这里不会因为 state.count 改变而重新执行
console.log('count =', count);
});
state.count++;
原因:reactive 的响应式建立在 Proxy 的 getter 上;你解构取出来的是一个“当时的值”,后续不再走 Proxy getter,自然也就不再被 track。
ref:把值变成可追踪的响应式引用
import { ref } from 'vue';
const count = ref(0);
count.value++;
什么时候用 ref:
- 基本类型(number/string/boolean)优先用
ref。 - 需要“可整体替换”的对象也常用
ref(例如const user = ref<User | null>(null))。
toRef:把对象的某个属性变成 ref(保持联动)
import { reactive, toRef, watchEffect } from 'vue';
const state = reactive({ count: 0 });
const count = toRef(state, 'count');
watchEffect(() => {
console.log(count.value); // 会随 state.count 变化
});
state.count++;
count.value++;
适用场景:
- 你只想“拿出一个字段”给别处用,但还要保持和源对象同步。
toRefs:批量把对象属性转成 ref(常用于 composable 返回值)
import { reactive, toRefs } from 'vue';
const state = reactive({ a: 1, b: 2 });
const { a, b } = toRefs(state);
典型场景:
- 在
useXxx()里维护一个reactive状态,然后return toRefs(state),让调用方可以解构使用,同时保持响应式。
常见追问
Q1:props 里为什么也常用 toRef/toRefs?
因为 props 是响应式对象(Vue 3),但你一旦解构就可能丢响应式。常见写法:
const props = defineProps<{ id: string }>();
const id = toRef(props, 'id');
Q2:ref 和 reactive 怎么选?
- 单值、需要整体替换:
ref - 多字段对象、喜欢
state.x形式:reactive - 需要解构但保响应式:
toRef/toRefs