解决了!请参阅下面的评论。
请帮我调试这个始终为空的引用值!我将这个codesandbox放在一起来帮助说明问题。蒂亚🙏
快速总结:
我在一对父/子组件中有一个水平滚动行。父级正在处理无限滚动,其中有一个需要补充的列表。目标是在
InfiniteScroll.vue
(父级)中注册 IntersectionObserver,并检索放置在 List.vue
(子级)中终端列表元素之后的模板引用。理想情况下,当 terminalRef
出现在 DOM 中时,IntersectionObserver 会触发它的回调,获取并重新填充列表。
因此 IntersectionObserver 应该能够检索孩子的
terminalRef.value
。根据 vue 文档,这可以通过使用渲染和暴露方法的插槽来实现,但我正在努力寻找一个综合示例,该示例结合了使用 render
语法通过插槽传递模板引用。经过一番摆弄,我已经得到了大部分功能,但来自 List.vue
的子引用在 InfiniteScroll.vue
中仍然为空。
注意:我按照文档中的说明进行操作 键入组件模板参考。
这是代码:
// InfinitScroll.vue
<script lang="ts">
import { ref, watch, onMounted, onBeforeUnmount, h, defineComponent } from 'vue'
import List from '../List/List.vue'
export default defineComponent({
components: { List },
emits: ['infinite'],
setup(props, { slots, emit }) {
const scrollContainer = ref<HTMLElement|null>(null)
const ListInstance = ref<InstanceType<typeof List>|null>(null)
const getTerminalRef = () => ListInstance.value?.$refs.terminalRef as HTMLDivElement|null
const observer = new IntersectionObserver((entries) => {
const entry = entries[0]
if (entry.isIntersecting) {
emit('infinite')
}
}, { root: scrollContainer.value, threshold: 0.9 })
onMounted(() => {
if (ListInstance.value) { // always null, but the ref is visible in vue dev tools
debugger // <---- Never stops here
const ref = ListInstance.value.$refs.terminalRef as HTMLDivElement
if (ref) {
observer.observe(ref)
}
}
return () => h(
'div',
{ ref: scrollContainer, class: 'scroll-container' },
h('slot', { name: 'List', ref: 'ListInstance' }, slots.default && slots.default())
)
}
})
</script>
// List.vue
import { ref, h, defineComponent, onMounted } from 'vue'
import { type UIshow } from '../../client-types'
import ListItem from './ListItem.vue'
export default defineComponent({
components: { ListItem },
props: ['genre', 'shows'],
setup({ genre, shows }: { genre: string, shows: Map<string, UIshow> }, { expose }) {
const terminalRef = ref<HTMLDivElement|null>(null)
expose({ terminalRef })
return () => h('div', { class: 'genre-row' }, { default: () => [
h('h2', { class: 'title' }, [genre]),
h('ul', { class: 'primary' }, [
...Array.from(shows).map(
([sortingName, show]) => {
return h('li', { key: sortingName }, [
h(ListItem, { show })
])
}),
h('li', null, h('div', { ref: terminalRef }, '.'))
])
]})
}
})
</script>
// Lists.vue iterates over multiple InfiniteScroll/List pairs
<template>
<template v-if="genreMap.size">
<div v-for="[ genre, shows ] of genreMap" :key="genre" class="all-lists">
<template v-if="shows.size">
<InfiniteScroll @infinite="loadPage">
<List
:genre="genre"
:shows="shows"
/>
</InfiniteScroll>
</template>
</div>
</template>
<template v-else>
<h1 class="title">Loading...</h1>
</template>
</template>
您需要定义
ListInstance
引用,因此在 InfinitScroll.vue 中,而不是:
h('slot', { name: 'List', ref: 'terminalRef' },
应该是
h('slot', { name: 'List', ref: ListInstance },
作为一个对象,而不是字符串,正如您在评论中所说。