在开发过程中,发现⼀个⻚⾯,有⼤量的横向表格数据。导致⻚⾯卡顿严重。⻚⾯中 DOM 元 素过多,导致渲染及其缓慢。
之所以会导致卡顿,因为⻚⾯元素过多。
推荐使用:JavaScript Data Grid: Documentation(插件ag-grid)
横向滚动,⽐纵向滚动渲染时间更⻓。因为 element ui 对于 el-table 的设计,上⾯表头部 分是⼀个table,下⾯body部分也是⼀个table。 纵向滚动,可以分⻚。横向滚动,分⻚就不好⽤了。 因此采⽤虚拟滚动模式。
什么是虚拟滚动
⼀般的滚动,给⼀个固定⾼度,超出内容,就会展⽰滚动条,可以滚动。 虚拟滚动其实没有滚动,只是通过监听滚轮向上或向下滚动距离,计算当前要显⽰ 的内容列表的索引。因此,渲染的只是视⼝+缓冲部分,元素要少很多。
如上图,我们只渲染可视区域能⻅到的3,4,5,6这⼏个元素,⽽其他的都不会被渲 染。
模拟滚动
由于只渲染“看得⻅”的部分,因此是没有原⽣滚动功能的。需要取模拟滚动⾏为。在⽤ 户滚动滑轮或者划动屏幕时,相应的根据滚动距离, “滚动”内容列表。(⾮真正的滚 动,根据滚动位置重新渲染可⻅的列表元素)。 当这个操作时间跨度⾜够⼩时,它看起来就像是在滚动⼀样。
这有点像我们在画帧动画⼀样,每次⽤户滑动造成偏移量改变,我们都会根据这个偏移 量去渲染新的列表元素。就像是在⼀帧⼀帧的播放动画⼀样,当两帧间隔⾜够⼩时,动 画看起来就会很流畅,就像是在滚动⼀样。
横向滚动
横向普通滚动
横向普通滚动
<template> <div class="container"> <div class="header"> <el-button @click="gernateData(10, 500)">500列</el-button> <el-button @click="gernateData(10, 1000)">1000列</el-button> </div> <!-- <div class="listWrapper" ref="listWrap" @scroll="onScroll"> --> <!-- <div class="listWrapper" ref="listWrap"> <div class="scrollWraper" ref="list" :style="{ width: `${allColumns.length * 150}px`, height: '1px' }" ></div> --> <el-table border :data="tableData" ref="tableBox" v-loading="loading"> <!-- force-scroll="horizontal" --> <el-table-column v-for="col in virtualColumns" :key="col.key" :prop="col.key" :label="col.name" width="150" align="right" :formatter="formatCellThousand" /> </el-table> <!-- </div> --> </div> </template> <script> export default { name: "normal-horizontal-scroll", data() { return { loading: false, tableData: [], allColumns: [], virtualColumns: [], startCol: 0, endCol: 20, }; }, computed: { // virtualColumns() { // return this.allColumns.slice(this.startCol, this.endCol) // } }, methods: { generateColumns(num) { const columns = []; for (let i = 0; i < num; i++) { columns.push({ key: `col${i}`, name: `列${i}`, }); } this.allColumns = columns; }, gernateData(rows, cols) { this.loading = true; setTimeout(() => { this.generateColumns(cols); const data = []; for (let row = 0; row < rows; row++) { const item = {}; for (let i = 0; i < cols; i++) { item[`col${i}`] = `row${row}-col${i}`; } data.push(item); } this.tableData = data; this.loading = false; this.calcColumns(cols); }, 500); }, formatCellThousand(row, column, cellValue) { return "$:" + cellValue; }, calcColumns(eCol) { const endCol = eCol || this.endCol; this.virtualColumns = this.allColumns.slice(this.startCol, endCol); }, onScroll() { const scrollLeft = this.$refs.listWrap.scrollLeft; console.log(scrollLeft); console.log(this.$refs.listWrap.scrollWidth); console.log(this.$refs.listWrap.clientWidth); const newIndex = Math.floor(scrollLeft / 150); if (newIndex !== this.startCol) { this.startCol = Math.floor(scrollLeft / 150); this.endCol = this.startCol + 20; this.$refs.tableBox.$el.style.marginLeft = `${scrollLeft}px`; this.calcColumns(); } if (scrollLeft === 0) { this.$refs.tableBox.$el.style.marginLeft = `${scrollLeft}px`; } }, }, }; </script> <style> .container { padding: 20px; } .header { margin-bottom: 20px; } .listWrapper .el-table__body-wrapper { overflow-x: hidden !important; } .listWrapper { overflow-x: auto; } </style>
横向虚拟滚动
<template> <div class="container"> <div class="header"> <el-button @click="gernateData(10, 500)">500列</el-button> <el-button @click="gernateData(10, 1000)">1000列</el-button> </div> <div class="listWrapper" ref="listWrap" @scroll="onScroll"> <div class="scrollWraper" ref="list" :style="{ width: `${allColumns.length * 150}px`, height: '1px' }" ></div> <el-table border :data="tableData" ref="tableBox" v-loading="loading" force-scroll="horizontal" > <el-table-column v-for="col in virtualColumns" :key="col.key" :prop="col.key" :label="col.name" width="150" align="right" :formatter="formatCellThousand" /> </el-table> </div> </div> </template> <script> export default { name: "index-home", data() { return { loading: false, tableData: [], allColumns: [], virtualColumns: [], startCol: 0, endCol: 20, }; }, computed: { // virtualColumns() { // return this.allColumns.slice(this.startCol, this.endCol) // } }, methods: { generateColumns(num) { const columns = []; for (let i = 0; i < num; i++) { columns.push({ key: `col${i}`, name: `列${i}`, }); } this.allColumns = columns; }, gernateData(rows, cols) { this.loading = true; setTimeout(() => { this.generateColumns(cols); const data = []; for (let row = 0; row < rows; row++) { const item = {}; for (let i = 0; i < cols; i++) { item[`col${i}`] = `row${row}-col${i}`; } data.push(item); } this.tableData = data; this.loading = false; this.calcColumns(); }, 500); }, formatCellThousand(row, column, cellValue) { return "$:" + cellValue; }, calcColumns() { this.virtualColumns = this.allColumns.slice(this.startCol, this.endCol); }, onScroll() { const scrollLeft = this.$refs.listWrap.scrollLeft; console.log(scrollLeft); console.log(this.$refs.listWrap.scrollWidth); console.log(this.$refs.listWrap.clientWidth); const newIndex = Math.floor(scrollLeft / 150); if (newIndex !== this.startCol) { this.startCol = Math.floor(scrollLeft / 150); this.endCol = this.startCol + 20; this.$refs.tableBox.$el.style.marginLeft = `${scrollLeft}px`; this.calcColumns(); } if (scrollLeft === 0) { this.$refs.tableBox.$el.style.marginLeft = `${scrollLeft}px`; } }, }, }; </script> <style> .container { padding: 20px; } .header { margin-bottom: 20px; } .listWrapper .el-table__body-wrapper { overflow-x: hidden !important; } .listWrapper { overflow-x: auto; } </style>
纵向滚动
纵向普通滚动
<template> <div class="container"> <div class="header"> <el-button @click="generateData(500)">500⾏</el-button> <el-button @click="generateData(1000)">1000⾏</el-button> </div> <el-table :data="tableData" v-loading="loading" max-height="528"> <el-table-column v-for="col in columnLength" :key="col" :prop="col + ''" :label="`第${col}列`" ></el-table-column> </el-table> </div> </template> <script> export default { name: "normal-vertical-scroll", data() { return { loading: false, tableData: [], columnLength: 10, }; }, methods: { generateData(row) { this.loading = true; setTimeout(() => { const columns = []; for (let i = 0; i < row; i++) { const obj = {}; for (let j = 0; j < this.columnLength; j++) { obj[j + 1] = `第${i + 1}⾏-第${j + 1}列`; } columns.push(obj); } this.loading = false; console.log(columns); this.tableData = columns; }, 500); }, }, }; </script> <style> .container { padding: 20px; } .header { margin-bottom: 20px; } </style>
纵向虚拟滚动
<template> <div class="container"> <div class="header"> <el-button @click="generateData(500)">500⾏</el-button> <el-button @click="generateData(1000)">1000⾏</el-button> </div> <div class="list-column-wrapper" ref="listWrap" @scroll="onScroll"> <div class="scroll-wrapper" ref="list" :style="{ height: `${allRows.length * 48}px`, width: '1px', }" ></div> <el-table :data="tableData" ref="tableBox" v-loading="loading"> <el-table-column v-for="col in columnLength" :key="col" :prop="col + ''" :label="`第${col}列`" ></el-table-column> </el-table> </div> </div> </template> <script> export default { name: "virtual-vertical-scroll", data() { return { loading: false, tableData: [], allRows: [], columnLength: 10, startRow: 0, endRow: 10, }; }, mounted() { this.$refs.tableBox.style.height = "528px"; }, methods: { calcRows(list) { console.log("startRow", this.startRow); console.log("endRow", this.endRow); this.tableData = list.slice(this.startRow, this.endRow); }, generateData(row) { this.loading = true; setTimeout(() => { const columns = []; for (let i = 0; i < row; i++) { const obj = {}; for (let j = 0; j < this.columnLength; j++) { obj[j + 1] = `第${i + 1}⾏-第${j + 1}列`; } columns.push(obj); } this.loading = false; this.allRows = columns; this.calcRows(this.allRows); }, 500); }, onScroll() { const scrollTop = this.$refs.listWrap.scrollTop; console.log(scrollTop); const newIndex = Math.floor(scrollTop / 48); if (newIndex !== this.startRow) { this.startRow = Math.floor(scrollTop / 48); this.endRow = this.startRow + 10; this.$refs.tableBox.$el.style.marginTop = `${scrollTop}px`; this.$refs.tableBox.$el.style.overflow = "visible"; this.calcRows(this.allRows); } if (scrollTop === 0) { this.$refs.tableBox.$el.style.marginTop = `${scrollTop}px`; } }, }, }; </script> <style> .container { padding: 20px; } .header { margin-bottom: 20px; } .list-column-wrapper .el-table__body-wrapper { overflow-y: hidden !important; } .list-column-wrapper { display: flex; overflow-y: auto; max-height: 528px; } </style>