我有一个 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>
您只需将引用添加到插槽的虚拟节点即可:
<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>
更通用的方法需要递归遍历节点来收集引用(尽管这不能处理现有引用和可能的插槽的情况,您可以自行改进)。这允许包装开槽组件,例如有条件地渲染它们:
<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>