【vue3 依赖注入】需要在onMounted中创建的跟DOM有关的元素如何依赖注入

196 阅读2分钟

经过多种尝试,最后我采用的方法是:在父组件里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);
});