我们将一个10000+条的数据通过el-collapse展示出来,同时在点开每一个item时,要内置一个编辑器,对文本内容进行编辑。其实,如果仅10000+条数据的文本的单独展示,可能性能不会太差,但由于每一条都需要带有一个文本编辑器,性能就存在很大的问题。
本文中采取了虚拟滚动的方式,来进行性能优化。
技术栈
- Vue3
- Element-Plus
实现
1. HTML
<template><div class="json" @scroll="handleScroll"><div class="content" :style="{ height: contentHeight }"><el-collapse accordion v-model="activeNames" :style="contentStyle"><el-collapse-item v-for="(item, index) in visibleData" :name="index"><template #title><span class="title-text">{{ item.text.trim() }}</span></template><monacoEditorv-model="(item as any).text"width="100%"height="250px"/></el-collapse-item></el-collapse></div></div>
</template>
说明:
monacoEditor
组件是一个编辑器handleScroll
是滚动方法contentHeight
为滚动面板的高度activeNames
为el-collapse-item当前展开的项目visibleData
为展示出来的数据,面板在滚动的过程中,该数据不断在变化
所以,JavaScript主要通过动态计算上述几个变量,来达到动态加载的目的。
2. JavaScript
第一步:定义变量
const pageSize = 15 // 每页展示的数据条数let visibleData = ref() // 可视数据let activeNames = ref(0)let scrollOffset = ref(0) // 滚动便宜
第二步:计算相关变量
// 展示每一个数据条目需要的高度
const itemHeight = computed(() => {return document.querySelector('.el-collapse-item__header')?.clientHeight || 50
})// 所有items的全量数据
const itemData = computed(() => {let temp_data = useKnowStore.checkJsonData// 由于在本页面中有一个搜索的功能,所以在过滤时需要考虑到搜索框中的内容let _temp = !props.search ? temp_data : temp_data.filter(item => item.text.trim().includes(props.search)) // 过滤出的数据return _temp
})// 通过上一步计算获取到的全量数据,计算出展示这些数据需要的全部高度
// 另外说明:在计算高度的时候,这里加了一个编辑器的高度,因为需求要求默认展开一个编辑器,所以需要加上这个高度
const contentHeight = computed(() => {const itemContent = 350 // 编辑器的高度return `${itemData.value.length * itemHeight.value + itemContent}px`
})// 计算滚动偏移量
// 重点:虚拟滚动就是通过这个css样式来实现的
const contentStyle = computed(() => {return {transform: `translateY(${scrollOffset.value}px)`,}
})
第三步:mounted阶段,visibleData 赋值
onMounted(() => {const line = xxxxx || 0 // 当前要展示的数据在全量数据中的indexif(line) {scrollOffset.value = line * itemHeight.value // 偏移量document.querySelector('.json')?.scrollTo(0, scrollOffset.value) // 滚动到} else {activeNames.value = 0 // 初始化activeNamesvisibleData.value = itemData.value.slice(0, Math.min(itemData.value.length, pageSize))}
})
第四步:定义滚动事件
// 滚动事件
const handleScroll = (event) => {event.preventDefault(); // 阻止默认的滚动行为const dom = document.querySelector('.json')const clientHeight = dom?.clientHeight // 滚动内容的总高度const scrollTop = event.target.scrollTop;scrollOffset.value = scrollTop;// 计算可见数据的起始索引和结束索引let startIndex = Math.floor(scrollTop / itemHeight.value);let endIndex = startIndex + Math.ceil(clientHeight / itemHeight.value) - 1;// 更新可见数据visibleData.value = itemData.value.slice(startIndex, endIndex + 1);// activeNamesconst line = useKnowStore.posLineNumber && useKnowStore.posLineNumber - 2 || 0if(typeof activeNames.value === 'string' && activeNames.value === '') returnactiveNames.value = Math.max(line - startIndex, 0)
}
3. CSS
<style lang="scss" scoped>
.json {overflow-y: auto;height: calc(100vh - 240px);.title-text {width: 100%;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;}
}
</style>
效果
最终实现效果就如下图所示,已知本次需求的数据量有10000+条,但在Element元素中,最多永远展示我们定义的条数15条。这样一来,滚动效率得到了非常大的提高