在 Vue3 中,provide
和 inject
是实现祖孙组件通信非常方便的方法。
但是,很多朋友在实际开发中,尤其是点击后再 provide 值时,很容易踩到一个隐形坑:子组件注入不到动态提供的值。
今天就通过一个真实案例,带大家梳理清楚这个问题,并给出最佳实践!
1. 问题描述
假设我们的需求是这样的:
-
父组件在点击按钮之后,向子组件和孙组件动态传递一个值
taskTypeResult1
。 -
子组件和孙组件需要在 onMounted 中拿到这个值,并根据变化做响应。
于是,我们一开始这么写:
父组件:
<script setup>
import { provide, ref } from 'vue';
const taskTypeResult = ref(null);
const handleClick = () => {
provide('taskTypeResult1', taskTypeResult.value); // 点击时再 provide
taskTypeResult.value = '点击后的数值';
};
</script>
<template>
<button @click="handleClick">点击注入值</button>
</template>
孙组件:
<script setup>
import { inject, onMounted } from 'vue';
const taskTypeResult1 = inject('taskTypeResult1');
onMounted(() => {
console.log(taskTypeResult1.value, '孙组件打印');
});
</script>
结果:
👀 无论怎么点击,孙组件始终拿不到最新的 taskTypeResult1
!
2. 问题分析
-
provide
/inject
是同步初始化机制。 -
子组件在 onMounted 时,依赖注入已经完成,如果那时
provide
还没执行,inject
就拿不到数据。 -
后续即使父组件点击后重新
provide
,子组件也不会再次注入,因为inject
只在组件初始化时生效一次。
简而言之:
🔥
inject
只读初始化时的provide
,不会动态重新注入!
3. 正确的做法
要实现动态传值,正确姿势是:
✅ 一开始就 provide
一个 ref 或 reactive 对象,
✅ 点击时,只去修改它的 .value
,
✅ 子孙组件注入的是响应式对象,自然能感知到变化!
父组件写法(改进版)
<script setup>
import { provide, ref } from 'vue';
const taskTypeResult = ref(null);
// 初始化时就提供出去(注意:provide的是ref对象本身!)
provide('taskTypeResult1', taskTypeResult);
const handleClick = () => {
taskTypeResult.value = '点击后的数值'; // 只修改.value
};
</script>
<template>
<button @click="handleClick">点击注入值</button>
</template>
孙组件写法(改进版)
<script setup>
import { inject, onMounted, watch } from 'vue';
const taskTypeResult1 = inject('taskTypeResult1');
onMounted(() => {
console.log(taskTypeResult1.value, '孙组件初次打印');
});
// 如果想监听变化
watch(taskTypeResult1, (newVal) => {
console.log(newVal, '孙组件监听到变化');
});
</script>
4. 小结
错误写法 | 正确写法 |
---|---|
点击时再 provide | 初始化时就 provide |
provide 传普通值(不是 ref/reactive) | provide 传 ref/reactive 对象 |
子组件拿不到变化 | 子组件 inject 后监听 .value |
5. 总结一句话
在 Vue3 中,想要动态传值到子孙组件,必须一开始就 provide 一个响应式对象(ref 或 reactive),后续通过修改 .value 实现数据变化。
这样就可以保证子孙组件随时拿到最新数据,再也不用担心注入失败了!
6. 拓展思考
-
如果项目比较大,可以考虑用 Pinia 代替 provide/inject,集中管理状态。
-
如果跨层级复杂,但只少量值传递,
provide/inject
更轻便,不需要引入全局状态库。 -
provide
也可以传递函数,不只是传值哦!
结尾
希望这篇文章能帮你彻底搞清楚 Vue3 中 provide/inject 动态传值的正确姿势!
如果觉得有帮助,欢迎点赞收藏!🚀🚀🚀