1、此功能已集成到TTable组件 和TSelectTable
2、最终效果(基于element-plus 的 el-table组件)
3、TTable或TSelectTable组件使用(只需要在标签中设置useVirtual即可)
4、源码(可以提取当做hooks方式来使用--具体看组件源码)
js
// 初始化数据
let state = reactive({
tableData: props.table.data
})
// 渲染实际高度的容器
const actualHeightContainerEl = ref<HTMLElement | any>(null)
// 用于偏移的元素选择器
const translateContainerEl = ref<HTMLElement | any>(null)
// 滚动容器的元素选择器
const scrollContainerEl = ref<HTMLElement | any>(null)
// 所有数据
const saveDATA: Ref<any[]> = ref([])
// 缓存已渲染元素的高度
const RenderedItemsCache: any = {}
watch(
() => props.table.data,
val => {
// console.log(111, val)
if (props.useVirtual) {
saveDATA.value = val
updateRenderData(0)
} else {
state.tableData = val
}
},
{ deep: true }
)
onMounted(() => {
if (props.useVirtual) {
saveDATA.value = props.table.data
actualHeightContainerEl.value = document.querySelector(
".t_table_use_virtual .el-scrollbar__view"
)
translateContainerEl.value = document.querySelector(".t_table_use_virtual .el-table__body")
scrollContainerEl.value = document.querySelector(".t_table_use_virtual .el-scrollbar__wrap")
scrollContainerEl.value?.addEventListener("scroll", handleScroll)
}
})
// 更新实际渲染数据
const updateRenderData = (scrollTop: number) => {
let startIndex = 0
let offsetHeight = 0
for (let i = 0; i < saveDATA.value.length; i++) {
offsetHeight += getItemHeightFromCache(i)
if (offsetHeight >= scrollTop) {
startIndex = i
break
}
}
// 计算得出的渲染数据(props.virtualShowSize初始化显示条数)
state.tableData = saveDATA.value.slice(startIndex, startIndex + props.virtualShowSize)
// 缓存最新的列表项高度
updateRenderedItemCache(startIndex)
// 更新偏移值
updateOffset(offsetHeight - getItemHeightFromCache(startIndex))
}
// 滚动事件
const handleScroll = (e: any) => {
// 渲染正确的数据
updateRenderData(e.target.scrollTop)
// console.log("滚动事件---handleScroll")
}
// 移除滚动事件
onBeforeUnmount(() => {
// console.log("移除滚动事件")
if (props.useVirtual) {
scrollContainerEl.value?.removeEventListener("scroll", handleScroll)
}
})
// 获取缓存高度,无缓存,取配置项的 itemHeight(设置每行高度50px)
const getItemHeightFromCache = (index: number | string) => {
const val = RenderedItemsCache[index]
return val === void 0 ? 50 : val
}
// 更新实际高度
const updateActualHeight = () => {
let actualHeight = 0
saveDATA.value.forEach((_, i) => {
actualHeight += getItemHeightFromCache(i)
})
actualHeightContainerEl.value!.style.height = actualHeight + "px"
}
// 更新偏移值
const updateOffset = (offset: number) => {
if (translateContainerEl.value && translateContainerEl.value.style) {
translateContainerEl.value!.style.transform = `translateY(${offset}px)`
}
}
// 更新已渲染列表项的缓存高度
const updateRenderedItemCache = (index: number) => {
// 当所有元素的实际高度更新完毕,就不需要重新计算高度
const shouldUpdate = Object.keys(RenderedItemsCache).length < saveDATA.value.length
if (!shouldUpdate) return
nextTick(() => {
// 获取所有列表项元素
const Items: HTMLElement[] = Array.from(
document.querySelectorAll(".t_table_use_virtual .el-table__row")
)
// 进行缓存
Items.forEach(el => {
if (!RenderedItemsCache[index]) {
RenderedItemsCache[index] = el.offsetHeight
}
index++
})
// 更新实际高度
updateActualHeight()
})
}