You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何调用默认插槽中Vue组件暴露的函数?

如何在Container组件中调用插槽子组件的暴露方法

问题分析

你当前在Container组件中通过useSlots().default()获取的是VNode虚拟节点,并非组件的实际实例,因此无法直接调用子组件暴露的interfaceFunction方法。

可行解决方案

方案1:通过容器Ref收集子组件实例

修改Container.vue,利用DOM容器的Ref获取插槽内的组件实例:

<script setup>
import { ref, onMounted, nextTick } from 'vue'

const containerRef = ref(null)
const childInstances = ref([])

async function collectInstances() {
  await nextTick()
  if (!containerRef.value) return
  childInstances.value = Array.from(containerRef.value.children)
    .map(el => el.__vueExposed)
    .filter(Boolean)
}

onMounted(collectInstances)

function callInterfaceFunction() {
  childInstances.value.forEach(instance => {
    instance?.interfaceFunction?.()
  })
}
</script>

<template>
  <button @click="callInterfaceFunction">Call from Container</button>
  <div ref="containerRef">
    <slot />
  </div>
</template>

方案2:使用Provide/Inject实现主动注册

让子组件主动向Container注册实例,更符合Vue组件通信规范:

修改Container.vue

<script setup>
import { provide, ref } from 'vue'

const childInstances = ref([])

provide('registerChild', instance => {
  childInstances.value.push(instance)
})

function callInterfaceFunction() {
  childInstances.value.forEach(instance => {
    instance?.interfaceFunction?.()
  })
}
</script>

<template>
  <button @click="callInterfaceFunction">Call from Container</button>
  <slot />
</template>

修改ChildA/ChildB.vue

<script setup>
import { inject, getCurrentInstance, onMounted } from 'vue'

const registerChild = inject('registerChild')
const instance = getCurrentInstance()

function interfaceFunction() {
  console.log("Called interfaceFunction A")
}

defineExpose({ interfaceFunction })

onMounted(() => {
  registerChild?.(instance?.exposed)
})
</script>

方案3:作用域插槽传递Ref

通过作用域插槽让父组件将子组件Ref传递给Container:

修改Container.vue

<script setup>
import { ref } from 'vue'

const childRefs = ref([])

function callInterfaceFunction() {
  childRefs.value.forEach(ref => {
    ref?.interfaceFunction?.()
  })
}
</script>

<template>
  <button @click="callInterfaceFunction">Call from Container</button>
  <slot :addRef="ref => childRefs.value.push(ref)" />
</template>

修改App.vue

<script setup>
import { ref} from 'vue'
import Container from './Container.vue'
import ChildA from './ChildA.vue'
import ChildB from './ChildB.vue'

const childa = ref(null)
const childb = ref(null)

function callInterfaceFunction() {
    childa.value.interfaceFunction()
}
</script>

<template>
    <button @click="callInterfaceFunction">Call from App</button>

    <Container>
        <template #default="{ addRef }">
            <ChildA ref="childa" :ref="addRef"/>
            <ChildB ref="childb" :ref="addRef"/>
        </template>
    </Container>
</template>

方案对比

  • 方案1:实现简单,但依赖Vue内部属性(__vueExposed),版本升级可能存在兼容性风险。
  • 方案2:遵循Vue设计理念,组件解耦性好,适合复杂场景。
  • 方案3:需要父组件配合,逻辑清晰,适合父组件需要控制子组件的场景。

内容的提问来源于stack exchange,提问作者Eric G

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 06:28:17