如何监听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
相关产品推荐
相关产品推荐

