Vue3组合式API如何调用插槽中子组件暴露的方法?
Vue3组合式API中调用插槽子组件方法的正确实现
你当前的问题在于,useSlots()获取到的是VNode虚拟节点对象,而非子组件的实际运行实例(Component Instance)。VNode只是用来描述组件结构的虚拟节点,不包含组件暴露的方法,所以即便子组件用defineExpose暴露了方法,也无法从VNode上访问到。
方案一:子组件主动注册实例(推荐,耦合度低)
通过provide/inject让子组件在挂载时主动将自身实例注册到父组件,父组件维护实例列表后即可调用方法。
Parent.vue 代码
<template> <div><slot/></div> </template> <script lang="ts" setup> import { ref, provide, onUnmounted } from 'vue'; // 存储子组件实例的数组 const childInstances = ref<any[]>([]); // 提供注册子组件的方法 provide('registerChild', (instance: any) => { childInstances.value.push(instance); }); // 提供注销方法,避免内存泄漏 provide('unregisterChild', (instance: any) => { const idx = childInstances.value.findIndex(item => item === instance); if (idx !== -1) childInstances.value.splice(idx, 1); }); // 调用所有子组件的目标方法 function callMethods() { childInstances.value.forEach(instance => { instance.methodToBeCalledByParent?.(); }); } </script>
SomeComponent.vue 代码
<template> <!-- 子组件内容 --> </template> <script lang="ts" setup> import { inject, getCurrentInstance, onMounted, onUnmounted } from 'vue'; const registerChild = inject('registerChild'); const unregisterChild = inject('unregisterChild'); // 获取当前组件实例的proxy const instance = getCurrentInstance()?.proxy; function methodToBeCalledByParent() { console.log(123); } // 暴露方法给父组件 defineExpose({ methodToBeCalledByParent }); // 挂载时注册实例 onMounted(() => { if (registerChild && instance) registerChild(instance); }); // 卸载时注销实例 onUnmounted(() => { if (unregisterChild && instance) unregisterChild(instance); }); </script>
方案二:遍历插槽VNode获取组件实例(无需子组件配合)
组件挂载完成后,插槽VNode的component.proxy属性会指向组件实例,此时可以过滤并收集实例。
Parent.vue 代码
<template> <div><slot/></div> </template> <script lang="ts" setup> import { useSlots, onMounted, ref } from 'vue'; const slots = useSlots() as any; const childInstances = ref<any[]>([]); // 必须在挂载后获取实例,此时VNode已关联到真实组件 onMounted(() => { const slotChildren = slots.default?.() || []; childInstances.value = slotChildren // 过滤文本、注释等非组件节点 .filter(vnode => typeof vnode.type !== 'string' && vnode.type !== Text) // 获取组件实例的proxy .map(vnode => vnode.component?.proxy) // 过滤无效实例 .filter(instance => instance); }); function callMethods() { childInstances.value.forEach(instance => { instance.methodToBeCalledByParent?.(); }); } </script>
注意事项
- 方案一的耦合度更低,适配动态插槽内容(如
v-if、v-for切换)更稳定; - 方案二需要注意时机,必须在
onMounted之后执行,若插槽内容动态变化,需要重新收集实例; - 两种方案都要求子组件通过
defineExpose暴露需要调用的方法。
内容的提问来源于stack exchange,提问作者oceanblue
相关产品推荐
相关产品推荐

