应用背景说明
有一个虚拟列表的需求,期望虚拟列表的列表项组件能由外部自行指定(但又不希望使用slot), 这样虚拟列表抽取为独立组件就成为了可能
h函数用法
渲染函数 & JSX | Vue.js (vuejs.org)
render函数用法
sfc写法实现
<!--
@file: RenderTest.vue
@author: pan
-->
<script lang="ts">
export default {
name: 'RenderTest',
}
</script>
<script setup lang="ts">
import { h, Component, onMounted, ref, render } from 'vue'
export interface ItemData {
id: number
contentData: unknown
}
const props = defineProps<{
/**
* 内部需要被渲染的组件
*/
itemComp: Component
/**
* 渲染组件时需要的数据
*/
itemData: ItemData
}>()
const vueCompContainerRef = ref<HTMLDivElement>()
onMounted(() => {
const dom = vueCompContainerRef.value
if (!dom) return
render(
// @ts-ignore
h(props.itemComp, {
id: props.itemData.id,
contentData: props.itemData.contentData,
}),
dom
)
})
</script>
<template>
<div ref="vueCompContainerRef"></div>
</template>
<style lang="scss" scoped></style>
使用
列表项组件:
<!--
@file: OneItem.vue
@author: pan
-->
<script lang="ts">
export default {
name: 'OneItem',
}
</script>
<script setup lang="ts">
defineProps<{
id: number
contentData: string
}>()
</script>
<template>
<div class="item" :class="{ blue: id % 2 === 0 }">
{{ id }}-{{ contentData }}
</div>
</template>
<style lang="scss" scoped>
.item {
border-bottom: 1px solid #333;
}
.blue {
background-color: lightblue;
}
</style>
整合使用
<!--
@file: FunTest.vue
@author: pan
-->
<script lang="ts">
export default {
name: 'FunTest',
}
</script>
<script setup lang="ts">
import OneItem from './test/OneItem.vue'
import RenderTest from './test/RenderTest.vue'
import { shallowRef } from 'vue'
const OneItemShallow = shallowRef(OneItem)
const list = new Array(100)
.fill(true)
.map((_, idx) => ({ id: idx, contentData: '内容:' + idx }))
</script>
<template>
<div class="outter">
<RenderTest
v-for="item in list"
:key="item.id"
:item-comp="OneItemShallow"
:item-data="item"
></RenderTest>
</div>
</template>
<style lang="scss" scoped>
.outter {
height: 500px;
overflow: auto;
}
.outter::-webkit-scrollbar {
width: 0 !important;
}
</style>