如何在Vue中获取插槽内元素的Ref?

作者站长头像
站长
· 阅读数 11

想要写一个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个回答
avatar
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文档

回复
likes
适合作为回答的
  • 经过验证的有效解决办法
  • 自己的经验指引,对解决问题有帮助
  • 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
  • 询问内容细节或回复楼层
  • 与题目无关的内容
  • “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容