使用虚拟滚动条优化通过el-collapse展示多条数据的性能问题

我们将一个10000+条的数据通过el-collapse展示出来,同时在点开每一个item时,要内置一个编辑器,对文本内容进行编辑。其实,如果仅10000+条数据的文本的单独展示,可能性能不会太差,但由于每一条都需要带有一个文本编辑器,性能就存在很大的问题。

本文中采取了虚拟滚动的方式,来进行性能优化。

技术栈

  • Vue3
  • Element-Plus

实现

1. HTML
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>
                    <monacoEditor
                        v-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

第一步:定义变量

typescript 复制代码
const pageSize = 15 // 每页展示的数据条数

let visibleData = ref() // 可视数据

let activeNames = ref(0)

let scrollOffset = ref(0) // 滚动便宜

第二步:计算相关变量

typescript 复制代码
// 展示每一个数据条目需要的高度
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 赋值

typescript 复制代码
onMounted(() => {
    const line = xxxxx || 0 // 当前要展示的数据在全量数据中的index
    if(line) {
        scrollOffset.value = line * itemHeight.value // 偏移量
        document.querySelector('.json')?.scrollTo(0, scrollOffset.value) // 滚动到
    } else {
        activeNames.value = 0 // 初始化activeNames
        visibleData.value = itemData.value.slice(0, Math.min(itemData.value.length, pageSize))
    }
})

第四步:定义滚动事件

typescript 复制代码
// 滚动事件
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);

    // activeNames
    const line = useKnowStore.posLineNumber && useKnowStore.posLineNumber - 2 || 0

    if(typeof activeNames.value === 'string' && activeNames.value === '') return
    activeNames.value = Math.max(line - startIndex, 0)
}
3. CSS
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条。这样一来,滚动效率得到了非常大的提高

相关推荐
不可能的是23 分钟前
Claude Code 子 Agent 机制全解:怎么跑起来、怎么被管理、怎么互不干扰
javascript
jeffwang26 分钟前
我做了个让 AI 看屏幕跑测试的工具,因为 Playwright 测不了我的 Flutter Web
前端
HSunR1 小时前
dify 搭建ai作业批改流
开发语言·前端·javascript
代码不加糖1 小时前
2026 跨境电商独立站实战:从 0 到 1 搭建高转化 SaaS 商城(附源码)
开发语言·前端·javascript
亲亲小宝宝鸭1 小时前
拖一拖控件,拖出个问卷(低代码平台)
前端·低代码
江南十四行1 小时前
ReAct Agent 基本理论与项目实战(一)
前端·react.js·前端框架
We་ct2 小时前
LeetCode 72. 编辑距离:动态规划经典题解
前端·算法·leetcode·typescript·动态规划
小呆呆6662 小时前
Codex 穷鬼大救星
前端·人工智能·后端
当时只道寻常3 小时前
Vue3 + IntersectionObserver 实现高性能图片懒加载
前端
用户617517157013 小时前
关于普通函数和箭头函数的this
javascript