核心问题:跨组件通信控制加载状态。父组件需要等待所有子组件的API数据请求完成后,才隐藏 loading。但子组件的数据请求在各自内部完成,父组件无法直接感知,通过provide和inject方法可以让父组件得到反馈。
案例:
<!-- 父组件 --> <template> <van-loading v-if="loading" class="loading" /> <template v-else> <div class="container"> <Banner></Banner> <!-- banner组件需要通过API获取数据 --> </div> </template> </template>
父组件需要等待子组件banner加载之后,才隐藏loading。这里子组件banner内部实际是有一个API的请求方法:
// 子组件 <script setup> import { inject, onMounted } from 'vue'; onMounted(async () => { try { const res = await fetchBanner(); //API请求 if (res.code === 0) { banners.value = res.data; } } catch (error) { console.error("Banner加载失败:", error); } finally { } }); </script>
构想是父组件页面加载的时候先显示loading动画,然后等待子组件API数据请求完毕了,再隐藏loading动画,然后正确的显示父组件页面。那么就需要子组件在完成数据请求之后“通知”父组件。这里可以使用provide/inject达到这个目的。当然除了父子组件关系,其他层级关系的组件也可以是使用provide/inject。
首先,需要在父组件内provide提供一个资源,这里是一个函数将给到子组件进行调用,这个函数作用是用来记录子组件加载完成这个事情(计数)。
//父组件 <script setup> import { ref, provide } from 'vue'; const loadedCount = ref(0); // 已完成加载的组件计数 const expectedLoadCount = 2; // 预期需要加载的子组件总数 // 注册组件加载完成的函数 // 当某子组件加载完成,这个函数将被执行一次 const registerComponentLoad = () => { loadedCount.value++; //计数器 console.log(`组件加载完成: ${loadedCount.value}/${expectedLoadCount}`); // 当所有子组件加载完成时关闭loading if (loadedCount.value >= expectedLoadCount) { loading.value = false; } }; // 将函数提供给所有后代组件 provide('registerComponentLoad', registerComponentLoad); </script>
子组件需要在合适的地方调用父组件给的资源,这里是计数函数:
//子组件 <script setup> import { ref,inject, onMounted } from 'vue'; const banners = ref([]) // 从父组件注入注册函数(默认空函数防止报错) const registerComponentLoad = inject('registerComponentLoad', () => {}); onMounted(async () => { try { const res = await fetchBanner(); //API请求 if (res.code === 0) { banners.value = res.data; } } catch (error) { console.error("Banner加载失败:", error); } finally { // 无论成功/失败都通知父组件"我已完成" registerComponentLoad(); } }); </script>
子组件在完成API请求,并将结果赋值给对应的响应式变量之后,去执行provide的函数。这样预期的效果就实现了。
最后总结一下,当组件之间通信,某个组件需要等待另外一个组件(或者多个组件)"反馈"一些信息之后再做动作,可以使用provide/inject方法完成。provide提供的资源可以是一个响应式数据,也可以是一个回调函数,资源的形式主要看其他组件实际需要提供者具体做什么。