Vue3中父组件onMounted时调用异步组件方法的解决方案
解决Vue通用异步组件暴露方法未就绪的问题
针对你编写的通用异步组件,父组件在onMounted中调用暴露的methodA时方法未就绪的问题,以下几种方案可以让组件使用者无需额外编写监听逻辑:
方案1:用Promise封装方法,支持异步等待
在通用组件内部,把方法就绪逻辑封装成Promise,让父组件可以通过await直接等待方法可用:
<script setup> import { defineAsyncComponent, defineProps, defineExpose, ref, computed, watch } from 'vue'; const props = defineProps({ component: { type: String }, }); const components = { componentOne: defineAsyncComponent(() => import('...')), componentTwo: defineAsyncComponent(() => import('...')), } const currentComponent = computed(() => components[props.component] || null) const currentComponentRef = ref(null); const methodAReady = ref(false); const methodA = ref(() => null); // 监听组件实例变化,就绪后更新状态 watch(currentComponentRef, async (newVal) => { if (newVal) { await new Promise(resolve => setTimeout(resolve, 0)); // 等待组件内部初始化完成 if (newVal.methodA) { methodA.value = newVal.methodA; methodAReady.value = true; } } }, { immediate: true }); // 封装等待方法,返回Promise const waitForMethodA = () => { return new Promise(resolve => { if (methodAReady.value) { resolve(methodA.value); } else { const unwatch = watch(methodAReady, (ready) => { if (ready) { resolve(methodA.value); unwatch(); } }); } }); }; defineExpose({ methodA, waitForMethodA }) </script> <template> <component :is="currentComponent" ref="currentComponentRef" /> </template>
父组件使用时,直接通过await调用:
<script setup> import { ref, onMounted } from 'vue'; import AsyncWrapper from './AsyncWrapper.vue'; const wrapperRef = ref(null); onMounted(async () => { const methodA = await wrapperRef.value.waitForMethodA(); methodA(); }); </script> <template> <AsyncWrapper component="componentOne" ref="wrapperRef" /> </template>
方案2:通过自定义事件主动通知父组件
通用组件在methodA就绪后,主动触发自定义事件通知父组件,父组件只需监听事件即可执行逻辑:
<script setup> import { defineAsyncComponent, defineProps, defineEmits, defineExpose, ref, computed, watch } from 'vue'; const props = defineProps({ component: { type: String }, }); const emit = defineEmits(['methodAReady']); const components = { componentOne: defineAsyncComponent(() => import('...')), componentTwo: defineAsyncComponent(() => import('...')), } const currentComponent = computed(() => components[props.component] || null) const currentComponentRef = ref(null); const methodA = ref(() => null); watch(currentComponentRef, async (newVal) => { if (newVal) { await new Promise(resolve => setTimeout(resolve, 0)); if (newVal.methodA) { methodA.value = newVal.methodA; emit('methodAReady', methodA.value); // 触发事件传递方法 } } }, { immediate: true }); defineExpose({ methodA }) </script> <template> <component :is="currentComponent" ref="currentComponentRef" /> </template>
父组件使用时,直接绑定事件处理:
<script setup> import { ref } from 'vue'; import AsyncWrapper from './AsyncWrapper.vue'; const handleMethodAReady = (methodA) => { methodA(); }; </script> <template> <AsyncWrapper component="componentOne" @method-a-ready="handleMethodAReady" /> </template>
方案3:优化内部监听逻辑,提前就绪时机
修改通用组件内部的监听逻辑,用watchEffect覆盖初始化和组件切换场景,确保方法赋值时机更早:
<script setup> import { defineAsyncComponent, defineProps, defineExpose, ref, computed, watchEffect, nextTick } from 'vue'; const props = defineProps({ component: { type: String }, }); const components = { componentOne: defineAsyncComponent(() => import('...')), componentTwo: defineAsyncComponent(() => import('...')), } const currentComponent = computed(() => components[props.component] || null) const currentComponentRef = ref(null); const methodA = ref(() => null); // 监听组件加载状态,就绪后立即赋值 watchEffect(async () => { if (currentComponent.value) { await nextTick(); // 等待组件挂载完成 if (currentComponentRef.value?.methodA) { methodA.value = currentComponentRef.value.methodA; } } }); defineExpose({ methodA }) </script> <template> <component :is="currentComponent" ref="currentComponentRef" /> </template>
这种方案下,父组件若要在onMounted中调用,可额外加一层nextTick确保时机正确,结合方案1的Promise封装会更可靠。
内容的提问来源于stack exchange,提问作者alec_lloyd_probert
相关产品推荐
相关产品推荐

