You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue中useSlots()获取插槽宽度仅首次有效问题排查与解决

问题描述

我通过如下代码获取插槽传入HTML元素的宽度:

<template>
    <div>
    <button @mouseover="state.show = true" @mouseleave="state.show = false">
      hover me
    </button>
    <div v-if="state.show">
        <slot name="test"></slot>      
    </div>
  </div>
</template>

<script setup>
import { reactive, watch, useSlots, nextTick } from 'vue'

const slots = useSlots();
const state = reactive({
  show: false
})  

watch(() => state.show, () => {
  if (state.show) {
    nextTick(() => {
      console.log(slots.test()[0].el.offsetWidth)
    })
  }
})
  
</script>

这段代码仅在首次hover按钮时输出正确宽度,后续hover均输出0。我还尝试在onUpdated钩子中使用getBoundingClientRect():

<script setup>
import { reactive, watch, useSlots, nextTick, onUpdated } from 'vue'

const slots = useSlots();
const state = reactive({
  show: false
})  
onUpdated(() => {
    if (state.show && slots.test() && slots.test()[0] && slots.test()[0].el) {
              console.log(slots.test()[0].el.getBoundingClientRect())   
    }

})
</script>

结果仍为仅首次hover显示正确宽度。请问出现该问题的原因是什么?如何实现每次hover按钮都能获取<slot name="test"></slot>的宽度?

问题原因
  • 插槽DOM引用失效:当v-if切换为false时,插槽所在的DOM容器会被销毁,但useSlots()返回的插槽对象里的el引用不会自动更新,后续hover时拿到的是已被移除的DOM节点,自然获取不到有效宽度。
  • useSlots()的特性限制:useSlots()返回的是当前渲染周期的插槽内容快照,首次渲染后如果插槽内容被销毁重建,旧的slots.test()[0].el无法指向新创建的DOM元素。
解决方案

放弃通过useSlots()获取DOM元素,改用**模板引用(ref)**绑定插槽容器,每次显示时通过ref获取最新的DOM节点:

<template>
    <div>
    <button @mouseover="state.show = true" @mouseleave="state.show = false">
      hover me
    </button>
    <div v-if="state.show" ref="slotContainer">
        <slot name="test"></slot>      
    </div>
  </div>
</template>

<script setup>
import { reactive, watch, ref, nextTick } from 'vue'

const slotContainer = ref(null);
const state = reactive({
  show: false
})  

watch(() => state.show, async () => {
  if (state.show) {
    await nextTick(); // 等待DOM完成渲染更新
    const slotElement = slotContainer.value?.firstElementChild;
    if (slotElement) {
      console.log('offsetWidth:', slotElement.offsetWidth);
      console.log('getBoundingClientRect宽度:', slotElement.getBoundingClientRect().width);
    }
  }
})
  
</script>

说明

  • 模板引用slotContainer会在每次v-if创建容器时指向最新的DOM节点,确保获取到的是当前页面中存在的元素。
  • nextTick()用于等待Vue完成DOM更新流程,保证插槽元素已经渲染到页面,此时获取的宽度值是准确的。

内容的提问来源于stack exchange,提问作者BT101

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 15:00:17