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

如何监听Vue Slots的内容变化?

如何监听Vue Slots的内容变化

问题场景

开发抽屉组件时,需要通过修改高度实现展开/收起状态,但必须感知插槽内容的外部修改(而非抽屉自身操作),避免因插槽高度变化导致的视觉不匹配问题。例如插槽内容从有到无、高度从100px变为200px时,抽屉需同步调整自身状态。

尝试过的无效方法

曾尝试深度监听this.$slots,但仅在初始化时触发,插槽内容变化时无响应:

watch: {
  '$slots': {
    handler() {
      console.log(this.$slots, 'slots')
    },
    deep: true,
    immediate: true,
  },
}

测试场景:插槽内通过v-if控制组件显示/隐藏,超时后组件消失,但未触发watch回调。

原因:$slots是Vue内部维护的只读对象,并非响应式数据,因此无法通过watch监听其变化。

可行解决方案

方案1:通过外部传入的slotKey触发更新

这是最简便的方案,适合插槽内容由外部受控的场景。外部通过更新slotKey告知抽屉组件插槽内容变化,内部监听该key执行状态调整。

抽屉组件代码

<template>
  <div class="drawer" :style="{ height: expand ? contentHeight : '0px' }">
    <div class="drawer-content">
      <slot></slot>
    </div>
  </div>
</template>

<script>
export default {
  props: ['expand', 'slotKey'],
  data() {
    return {
      contentHeight: '100px'
    }
  },
  watch: {
    slotKey() {
      this.$nextTick(() => {
        // 重新计算插槽内容高度
        const height = this.$el.querySelector('.drawer-content').offsetHeight
        this.contentHeight = `${height}px`
        // 此处可添加其他状态调整逻辑
      })
    }
  }
}
</script>

外部使用示例

<test-drawer :expand="openNow" :slot-key="timeout ? 'after' : 'before'">
  <a-test-component v-if="!timeout"></a-test-component>
</test-drawer>

<script>
export default {
  data() {
    return {
      openNow: true,
      timeout: false
    }
  },
  mounted() {
    setTimeout(() => {
      this.timeout = true
    }, 5000)
  }
}
</script>

方案2:利用updated钩子结合DOM状态判断

通过组件的updated钩子检查插槽内容的DOM状态变化,同时通过标志位排除抽屉自身操作导致的更新。

<script>
export default {
  data() {
    return {
      contentHeight: '100px',
      ignoreUpdate: false // 标记是否为自身操作触发的更新
    }
  },
  methods: {
    toggleExpand() {
      this.ignoreUpdate = true
      this.expand = !this.expand
      this.$nextTick(() => {
        this.ignoreUpdate = false
      })
    }
  },
  updated() {
    if (this.ignoreUpdate) return
    
    const container = this.$el.querySelector('.drawer-content')
    const currentHeight = container.offsetHeight
    if (currentHeight !== parseInt(this.contentHeight)) {
      this.contentHeight = `${currentHeight}px`
      // 执行状态调整逻辑
    }
  }
}
</script>

方案3:简化版MutationObserver监听

针对动态不可控的插槽内容,可在组件内部给插槽容器添加MutationObserver,仅监听插槽区域的DOM变化,实现精确感知。

<script>
export default {
  mounted() {
    const container = this.$el.querySelector('.drawer-content')
    const observer = new MutationObserver(() => {
      this.$nextTick(() => {
        const newHeight = container.offsetHeight
        this.contentHeight = `${newHeight}px`
      })
    })

    // 监听子节点变化、子树变化及属性变化
    observer.observe(container, {
      childList: true,
      subtree: true,
      attributes: true
    })

    // 组件销毁时停止监听,避免内存泄漏
    this.$once('hook:beforeDestroy', () => {
      observer.disconnect()
    })
  }
}
</script>

总结

  • 不推荐监听$slots,因其非响应式对象;
  • 优先使用方案1,实现简单且可控;
  • 动态插槽场景可选择方案3,监听范围精准,实现成本并不高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 12:35:04