经过多种尝试,最后我采用的方法是:在父组件里provide一个方法返回要传递的值,在孙辈inject这个方法,再执行方法,由于我需要传递的值是对象,得到的是Proxy,而我需要操作原始对象,因此进行了toRaw。以下是踩坑过程,记录仅供自己学习翻阅。
//父组件
let viewer = ref();
provide("emitViewer", () => {
return viewer.value;
});
const getViewer = (value) => {
viewer.value = value;
};
//孙辈
import { ref, inject, watch, getCurrentInstance, toRaw } from "vue";、
const getViewerFn = inject("emitViewer");
const viewer = toRaw(getViewerFn());
问题背景
我需要传递给后代的对象是需要在获取到div之后才能创建的,因此在onMounted中才有值,但在onMounted中就算只是provide一个字符串,在孙辈中也是undefined,代码如下:
import {onMounted, provide} from "vue";
//如果在这执行provide,传递如下字符串是能在孙辈获取到的
provide("out", "hello");
onMounted(() => {
//在这孙辈打印是undefined,考虑到生命周期的影响,孙辈inject下面这个时,是通过按钮点击再inject的
provide("in", "hello");
})
解决措施
针对这个问题,我的做法是先把需要在onMounted中才能创建的对象通过props传到下一级子组件中:
//Father
<template>
<Child :obj="obj"></Child>
</template>
<script setup>
import { ref, onMounted } from "vue";
let obj = ref();
onMounted(() => {
obj=new Obj({
container: "container",
...
})
})
</script>
在子组件里再provide:
//Child
<script setup>
import { provide } from "vue";
const props = defineProps(["obj"]);
provide("obj", props.obj);
</script>
以上步骤操作完成后,在孙辈就可以正常inject获取到了。
import { inject } from "vue";
let obj = inject("obj");
console.log("test", obj);
结语
这个做法虽然暂时解决了我的问题,但感觉不是最优解,希望有大佬看到后可以指点一二。
尝试过的
兄弟组件通信
方法一:兄弟1将需要在dom渲染后才能创建的值传到父组件,父组件prop到兄弟二,兄弟二监听值的变化,此时只能在监听里获取到,在监听外面是undefined。provide无法在watch里面,导致兄弟二的后来无法通过依赖注入获取到该值。
//Bro1.vue
let viewer;
const emit = defineEmits(["viewer"]);
onMounted(() => {
viewer=...
emit("viewer", viewer);
})
//Father.vue
<template>
<Bro1 @viewer="getViewer"> </Bro1>
<Bro2 :viewer="viewer"></Bro2>
</template>
<script setup>
const getViewer = (value) => {
viewer.value = value
};
</script>
//Bro2.vue
const props = defineProps(["viewer"]);
provide("viewer", props.viewer);
console.log("child", props.viewer);
watch(
() => props.viewer,
(value, preValue) => {
console.log("watch", props.viewer);
}
);
方法二:首先在main.ts中导入mitt,父组件按方法一的方法获取到了viewer值后,通过$mitt传值,在兄弟二中接收,但依然只有在事件里能获取到值,跟方法一一样的结果。
import mitt from "mitt";
app.config.globalProperties.$mitt = mitt();
//Father.vue
<template>
<Bro1 @viewer="getViewer"> </Bro1>
<Bro2 ></Bro2>
</template>
<script setup>
const getViewer = (value) => {
appContext.config.globalProperties.$mitt.emit(
"viewerEvent",
(viewer.value = value)
);
};
</script>
//Bro2.vue
const amount = ref();
const { appContext } = getCurrentInstance() as ComponentInternalInstance;
appContext.config.globalProperties.$mitt.on("viewerEvent", (res: number) => {
amount.value = res;
console.log("sss", amount.value);
});