如何等待Vue插槽内容渲染后判断组件是否可滚动?
Vue中等待插槽渲染完成后判断容器是否可滚动的解决方案
问题背景
开发BitBox组件时,需要判断外层容器#bit-box是否可滚动(通过scrollHeight > clientHeight判断)。容器内容通过Vue插槽传入,插槽数据页面加载初始即可获取无需AJAX,但在组件的onMounted钩子中调用isScrollable函数始终返回false,页面完全渲染后在控制台手动调用则返回true。
滚动判断函数:
export function isScrollable(ele){ return ele.scrollHeight > ele.clientHeight; }
BitBox组件核心代码:
const bitBox = shallowRef<HTMLDivElement>(); onMounted((): void => { if (typeof bitBox.value === 'undefined'){ return; } // DOMContentLoaded此时已触发,不会执行 document.addEventListener('DOMContentLoaded', function () { alert('WE ARE READY'); }); // 此时插槽内容未渲染完成,判断错误 if(isScrollable(bitBox.value)){ alert('Scrollable Yeeeah 1'); } else { alert('Scrollable Naaah 1'); } });
<template> <div ref="bitBox" id="bit-box"> <slot name="content"></slot> </div> </template>
组件使用方式:
<template> <BitBox> <template #content> <p v-for="someData in dataset" :key="someData.id"> {{ someData.title }} </p> </template> </BitBox> </template>
原因分析
Vue的onMounted钩子触发时机是组件自身DOM挂载完成,但插槽内容(尤其是包含v-for的列表)属于父组件传递的内容,其渲染完成时机晚于子组件的onMounted。此时容器的scrollHeight还未更新为插槽内容渲染后的实际高度,导致isScrollable判断错误。
另外,DOMContentLoaded事件在页面初始HTML加载解析完成后就会触发,而onMounted是在组件挂载时执行,此时该事件早已触发,所以添加监听不会生效。
解决方案
方法1:使用nextTick等待DOM更新
nextTick会在Vue的DOM更新循环结束后执行回调,此时插槽内容已完成渲染,容器的scrollHeight已更新。
修改BitBox组件的onMounted逻辑:
const bitBox = shallowRef<HTMLDivElement>(); onMounted((): void => { if (!bitBox.value) return; nextTick(() => { if(isScrollable(bitBox.value)){ alert('Scrollable Yeeeah'); } else { alert('Scrollable Naaah'); } }); });
方法2:监听插槽内容的变化(可选,适用于数据动态更新场景)
如果插槽内容依赖的数据可能动态更新,可通过监听数据变化,再结合nextTick重新判断:
在使用BitBox的父组件中:
const dataset = ref([/* 初始数据 */]); watch(dataset, () => { nextTick(() => { const bitBoxEle = document.getElementById('bit-box'); if (bitBoxEle && isScrollable(bitBoxEle)) { // 处理滚动逻辑 } }); }, { deep: true });
方法3:使用MutationObserver监听容器变化
如果需要更精确地监听容器内容的DOM变化(比如插槽内容可能通过其他方式动态添加),可以使用MutationObserver:
在BitBox组件的onMounted中:
const bitBox = shallowRef<HTMLDivElement>(); onMounted((): void => { if (!bitBox.value) return; const observer = new MutationObserver(() => { if(isScrollable(bitBox.value)){ alert('Scrollable Yeeeah'); observer.disconnect(); // 判断完成后停止监听 } }); // 监听容器的子节点变化 observer.observe(bitBox.value, { childList: true, subtree: true }); });
内容的提问来源于stack exchange,提问作者ii iml0sto1
相关产品推荐
相关产品推荐

