如何在Vue中获取插槽内元素的Ref?
想要写一个Popover,计算元素位置使用的是floating-ui
,需要获取触发元素的Ref和Popover内容的Ref来进行定位。
触发元素和内容通过插槽传入,遇到的问题是如何获取插槽内元素的Ref
Popover 伪代码:
<template>
<slot name="trigger" />
<teleport to="body">
<div v-if="show" class="absolute inset-0">
<slot />
</div>
</teleport>
</template>
使用:
<Popover>
<template #trigger>
<button >test</button>
</template>
<div>
Popover
</div>
</Popover>
目前我能想到的办法就是在触发元素和内容外套一层div,然后用div的Ref
<script setup>
const triggerRef = ref()
const floatingRef = ref()
</script>
<template>
<div ref="triggerRef">
<slot name="trigger" />
</div>
<teleport to="body">
<div ref="floatingRef" v-if="show" class="absolute inset-0">
<slot />
</div>
</teleport>
</template>
但是这样就多了一层节点,在想能不能有更好的解决方案?
回复
1个回答
test
2024-06-21
看了别的组件库,目前已经找到一种解决方案:
就是直接通过 useSlots()
获取插槽的 vNode,然后手动渲染,并绑定ref。
代码如下:
<script setup>
import {ref, useSlots} from 'vue'
const triggerRef = ref()
const floatingRef = ref()
const slots = useSlots()
const triggerArr = slots?.trigger?.()
// 获取vNode(真实场景,这一步需要校验插槽传过来的值。如果值是字符串或多个元素等情况,需要进一步处理)
const TriggerVNode = triggerArr[0]
</script>
<template>
<TriggerVNode ref="triggerRef" />
<teleport to="body">
<div ref="floatingRef" v-if="show" class="absolute inset-0">
<slot />
</div>
</teleport>
</template>
但是这样有个缺点,WebStorm 写插槽的时候不会有提示,甚至有警告
更新
以上问题可以通过 Vue3.3 新增宏 defineSlots()
来解决,defineSlots()
用于定义插槽。具体使用 查看Vue文档
回复
适合作为回答的
- 经过验证的有效解决办法
- 自己的经验指引,对解决问题有帮助
- 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
- 询问内容细节或回复楼层
- 与题目无关的内容
- “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容