如何在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>

但是这样就多了一层节点,在想能不能有更好的解决方案?

阅读 2.2k
avatarAI BotBETA

在Vue中,你可以使用ref()函数来获取插槽内元素的引用。ref()函数返回一个响应式引用,可以用来访问DOM元素或组件实例。

在你的例子中,你可以在Popover组件的setup()函数中创建一个ref,并将其绑定到插槽元素上。然后,你可以在模板中使用ref来获取插槽内元素的引用。

以下是一个示例代码:

<template>
  <slot name="trigger" ref="triggerRef" />
  <teleport to="body">
    <div v-if="show" class="absolute inset-0">
      <slot ref="popoverContent" />
    </div>
  </teleport>
</template>

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

const triggerRef = ref(null);
const popoverContent = ref(null);
</script>

在上面的代码中,我们在setup()函数中创建了两个reftriggerRefpopoverContent。然后,我们将这两个ref分别绑定到插槽元素上,通过在插槽标签上添加ref属性并指定对应的引用名称。

现在,你可以通过triggerRef.valuepopoverContent.value来访问插槽内元素的引用。注意,在使用引用时需要添加.value来获取实际的值。

使用这种方法,你就不需要在触发元素和内容外套一层div来获取引用了。

1 个回答

看了别的组件库,目前已经找到一种解决方案:

就是直接通过 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文档

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏