Vue3 Composition API 父组件访问子组件ref的实现方法
问题原因
<script setup> 语法默认存在作用域闭包,组件内部声明的ref、变量等成员不会自动对外暴露,父组件无法直接访问子组件内部的mainContentRef。另外你代码中mainContentRef初始值设为0是错误写法,DOM类型的ref初始值应当设为null。
方案1:通过defineExpose暴露子组件内部ref
如果确实需要在父组件中直接拿到滚动容器DOM,可以通过Vue提供的defineExpose宏手动暴露子组件内部成员,步骤如下:
- 修改子组件代码,修正ref初始值,添加暴露逻辑:
<!-- MainContent 子组件 --> <template> <div id="main-content" ref="mainContentRef"> <slot name="content"></slot> </div> </template> <script setup> import { ref } from 'vue'; // DOM引用初始值设为null const mainContentRef = ref(null); // 手动暴露指定成员给父组件 defineExpose({ mainContentRef }) </script>
- 父组件中给子组件标签绑定ref,在组件挂载完成后即可拿到DOM实例,绑定滚动事件:
<!-- 父组件 --> <template> <PageWrap> <template v-slot:content> <!-- 给子组件标签绑定ref,用于获取子组件实例 --> <MainContent ref="mainContentCompRef"> <template v-slot:content /> </MainContent> </template> </PageWrap> </template> <script setup> import { ref, onMounted, onBeforeUnmount } from 'vue'; // 子组件实例引用 const mainContentCompRef = ref(null); const handleScroll = (e) => { const scrollContainer = e.target; // 滚动到底部判定逻辑,阈值可根据业务调整 if (scrollContainer.scrollTop + scrollContainer.clientHeight >= scrollContainer.scrollHeight - 20) { // 执行fetch拉取更多数据 } } onMounted(() => { // 从子组件实例上拿到暴露的DOM,绑定滚动事件 const scrollEl = mainContentCompRef.value.mainContentRef; scrollEl?.addEventListener('scroll', handleScroll); }) // 组件卸载前移除事件监听,避免内存泄漏 onBeforeUnmount(() => { const scrollEl = mainContentCompRef.value?.mainContentRef; scrollEl?.removeEventListener('scroll', handleScroll); }) </script>
方案2:子组件派发自定义事件(更推荐)
如果只是需要在滚动触底时触发父组件的拉取逻辑,完全没必要跨组件传递DOM引用,这种方案耦合度更低:子组件自己维护内部DOM的事件监听,滚动到指定位置时对外抛出业务语义的事件,父组件只需要监听对应事件执行业务逻辑即可,不需要关心子组件的内部DOM结构,后续子组件调整滚动容器时父组件代码无需修改。
- 子组件代码:
<!-- MainContent 子组件 --> <template> <div id="main-content" ref="mainContentRef"> <slot name="content"></slot> </div> </template> <script setup> import { ref, onMounted, onBeforeUnmount } from 'vue'; const emit = defineEmits(['scroll-reach-bottom']); const mainContentRef = ref(null); const handleContainerScroll = (e) => { const scrollContainer = e.target; if (scrollContainer.scrollTop + scrollContainer.clientHeight >= scrollContainer.scrollHeight - 20) { emit('scroll-reach-bottom'); } } onMounted(() => { mainContentRef.value?.addEventListener('scroll', handleContainerScroll); }) onBeforeUnmount(() => { mainContentRef.value?.removeEventListener('scroll', handleContainerScroll); }) </script>
- 父组件代码:
<!-- 父组件 --> <template> <PageWrap> <template v-slot:content> <!-- 直接监听子组件抛出的业务事件 --> <MainContent @scroll-reach-bottom="fetchMoreList"> <template v-slot:content /> </MainContent> </template> </PageWrap> </template> <script setup> const fetchMoreList = () => { // 执行拉取更多数据的请求逻辑 } </script>
内容的提问来源于stack exchange,提问作者Bryan88
相关产品推荐
相关产品推荐

