注入 ref 后必须通过 .value 访问值,因为 provide/inject 传递的是 ref 对象本身而非解包值;模板中自动解包是编译器糖,js 逻辑中需显式使用 .value 以保持响应性。

Provide 注入 ref 后,在 inject 侧仍然需要 .value 访问值——这不是遗漏或错误,而是 Vue 响应式设计的必然结果。因为 provide/inject 传递的是 ref 对象本身,不是自动解包后的原始值;模板中自动解包的语法糖不延伸到 JS 逻辑里,inject 得到的仍是那个带 .value 的响应式包装对象。
provide 传的是 ref 对象,不是“解包后”的值
当你写 provide('count', count),其中 count 是 ref(0) 创建的,你实际提供出去的就是整个 ref 对象(比如 { value: 0, __v_isRef: true })。Vue 不会对它做额外解包或转换,只是原样传递引用。
- 这和
provide('count', count.value)完全不同:后者传的是数字0,彻底丢失响应性,inject 拿到的就是普通静态值 - 而传 ref 对象,才能让 inject 方通过
.value读写,并被 Vue 的依赖系统追踪
inject 拿到的是 ref,不是原始类型
const count = inject('count') 返回的仍是 ref 实例,不是 0 或 {} 这类原始值。所以:
- 读取必须用
count.value,否则得到的是整个对象(如{ value: 5 }) - 修改必须用
count.value = 10,直接赋值count = ref(10)会切断响应链接 - 在
watch或computed中也一样:要监听变化,就得传count(ref 本身),Vue 内部会自动读取其.value做依赖收集
为什么模板里不用 .value?仅限模板的编译器糖
如果你在 inject 后把 ref 暴露给模板(例如 return { count }),那么 {{ count }} 能直接显示数值,是因为 Vue 模板编译器在编译阶段识别了 ref 类型,自动转成 count.value。但这只发生在 <template></template> 内部。
- JS 逻辑中没有编译器介入,
console.log(count)打印的是 ref 对象,console.log(count.value)才是真实值 - 即使在
setup()里用toRefs解构,也得确保解构的是 ref 对象本身,而不是它的.value
常见误区与安全写法
容易踩坑的地方集中在“以为注入后就自动解包”:
- ❌ 错误:
const count = inject('count'); count++→ 报错,因为count是对象,不能直接 ++ - ❌ 错误:
const { value } = inject('count');→ 解构出的value是普通变量,后续赋值不触发响应更新 - ✅ 正确:
const count = inject('count'); count.value++ - ✅ 更健壮:加非空校验
const count = inject('count'); if (count) count.value = newValue;









