如何在默认槽中调用 vue 组件的公开函数?

问题描述 投票:0回答:1

我有一个 vue 游乐场

ChildA
ChildB
都公开了一个名为
interfaceFunction
的函数。

我可以通过获取对组件的引用来从

App
调用此函数。有了引用,我就可以直接调用该函数了。

我希望

Container
能够在其默认插槽中调用子级的此函数。
Container
如何获取组件的引用并调用函数?

应用程序.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)

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

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

    <Container>
        <ChildA ref="childa"/>
        <ChildB />
    </Container>
</template>

容器.vue

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

const slots = useSlots()

function callInterfaceFunction() {
  const children = slots.default()

  for ( const child of children ) {
    child.interfaceFunction()
  }
}
</script>

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

  <slot />
</template>

ChildA.vue

<script setup>

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

defineExpose({ interfaceFunction })
</script>

<template>
  <div>
    ChildA
  </div>
</template>

ChildB.vue

<script setup>

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

defineExpose({ interfaceFunction })
</script>

<template>
  <div>
    ChildB
  </div>
</template>
javascript vue.js vue-composition-api vuejs-slots
1个回答
0
投票

您只需将引用添加到插槽的虚拟节点即可:

VUE SFC 游乐场

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

const slots = useSlots();
let $children;
const slotted = () => ($children = [], slots.default().map((vnode, i) => h(vnode, {ref: $children[i] ??= ref()})));

function callInterfaceFunction(){
  $children.forEach(({value: child}) => child.interfaceFunction());
}
</script>

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

更通用的方法需要递归遍历节点来收集引用(尽管这不能处理现有引用和可能的插槽的情况,您可以自行改进)。这允许包装开槽组件,例如有条件地渲染它们:

VUE SFC 游乐场

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

const slots = useSlots();
let $children;
const traverse = vnode => {
  vnode = h(vnode, {ref: $children[$children.length] ??= ref()});
  vnode.children = vnode.children?.map(traverse);
  return vnode;
};
const slotted = () => ($children = [], slots.default().map(traverse));

function callInterfaceFunction(){
  $children.forEach(({value:child}) => child.interfaceFunction?.());
}
</script>

<template>
  <button @click="callInterfaceFunction">Call from Container</button>
  <slotted/>
</template>
<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 swapped = ref(false);

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

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

    <Container>
        <template v-if="!swapped"><ChildA/><ChildB/></template>
        <template v-else="swapped"><ChildB/><ChildA/></template>
    </Container>
    <button @click="swapped = !swapped">Swap</button>
</template>
© www.soinside.com 2019 - 2024. All rights reserved.