一、此功能已集成到TTable組件中
二、最終效果
三、需求
某些頁面不做分頁時,當數(shù)據(jù)過多,會導(dǎo)致頁面卡頓,甚至卡死
四、虛擬滾動
一、固定一個
可視區(qū)域
的大小并且其大小是不變的,那么要做到性能最大化就需要盡量少地渲染 DOM 元素,而這個最小值也就是可視范圍內(nèi)需要展示的內(nèi)容,而可視區(qū)域之外的元素均可以不做渲染。
二、如何計算可視區(qū)域內(nèi)需要渲染的元素,我們通過如下幾步來實現(xiàn)虛擬滾動:1、每一行的高度需要相同,方便計算。
2、需要知道渲染的數(shù)據(jù)量(數(shù)組長度),可基于總量和每個元素的高度計算出容器整體的所需高度,這樣就可以偽造一個真實的滾動條。
3、獲取可視區(qū)域的高度。
4、在滾動事件觸發(fā)后,滾動條的距頂距離即這個數(shù)據(jù)量中的偏移量,再根據(jù)可視區(qū)域本身的高度,算出本次偏移量,這樣就得到了需要渲染的具體數(shù)據(jù)
五、具體實現(xiàn)(源碼)
<template>
<div class="t-table" id="t_table">
<el-table
ref="el-table"
:data="tableData"
:class="{
cursor: isCopy,
row_sort: isRowSort,
highlightCurrentRow: highlightCurrentRow,
radioStyle: table.firstColumn && table.firstColumn.type === 'radio',
treeProps: isShowTreeStyle,
is_sort_icon:onlyIconSort
}"
:max-height="useVirtual?maxHeight||540:maxHeight"
v-bind="$attrs"
v-on="$listeners"
:highlight-current-row="highlightCurrentRow"
:border="table.border || isTableBorder"
:span-method="spanMethod || objectSpanMethod"
:cell-class-name="cellClassNameFuc"
@sort-change="soltHandle"
@row-click="rowClick"
@cell-dblclick="cellDblclick"
>
<!-- 主體內(nèi)容 -->
<template v-for="(item, index) in renderColumns">
<el-table-column
v-if="item.isShowCol === false ? item.isShowCol : true"
:key="index + 'i'"
:type="item.type"
:label="item.label"
:prop="item.prop"
:min-width="item['min-width'] || item.minWidth || item.width"
:sortable="item.sort || sortable"
:align="item.align || 'center'"
:fixed="item.fixed"
:show-overflow-tooltip="useVirtual?true:item.noShowTip?false:true"
v-bind="{ ...item.bind, ...$attrs }"
v-on="$listeners"
>
<template slot-scope="scope">
...
</template>
</el-table-column>
</template>
</el-table>
</div>
</template>
<script>
export default {
name: 'TTable',
props: {
// table所需數(shù)據(jù)
table: {
type: Object,
default: () => {
return {}
}
// required: true
},
// 表頭數(shù)據(jù)
columns: {
type: Array,
default: () => {
return []
}
// required: true
},
...
// Table最大高度
maxHeight: {
type: [String, Number]
},
// 是否開啟虛擬列表
useVirtual: {
type: Boolean,
default: false
}
},
data() {
return {
tableData: this.table?.data,
/**
* 虛擬列表
*/
saveDATA: [], // 所有數(shù)據(jù)
tableRef: null, // 設(shè)置了滾動的那個盒子
tableWarp: null, // 被設(shè)置的transform元素
fixLeft: null, // 固定左側(cè)--設(shè)置的transform元素
fixRight: null, // 固定右側(cè)--設(shè)置的transform元素
tableFixedLeft: null, // 左側(cè)固定列所在的盒子
tableFixedRight: null, // 右側(cè)固定列所在的盒子
scrollTop: 0,
scrollNum: 0, // scrollTop / (itemHeight * pageList)
start: 0,
end: 30, // 3倍的pageList
starts: 0, // 備份
ends: 30, // 備份
pageList: 10, // 一屏顯示
itemHeight: 48 // 每一行高度
}
},
watch: {
'table.data': {
handler(val) {
if (this.useVirtual) {
this.saveDATA = val
this.tableData = this.saveDATA.slice(this.start, this.end)
} else {
this.tableData = val
}
},
deep: true // 深度監(jiān)聽
},
scrollNum(newV) {
// 因為初始化時已經(jīng)添加了3屏的數(shù)據(jù),所以只有當滾動到第3屏時才計算位移量
if (newV > 1) {
this.start = (newV - 1) * this.pageList
this.end = (newV + 2) * this.pageList
requestAnimationFrame(() => {
// 計算偏移量
this.tableWarp.style.transform = `translateY(${this.start *
this.itemHeight}px)`
if (this.fixLeft) {
this.fixLeft.style.transform = `translateY(${this.start *
this.itemHeight}px)`
}
if (this.fixRight) {
this.fixRight.style.transform = `translateY(${this.start *
this.itemHeight}px)`
}
this.tableData = this.saveDATA.slice(this.start, this.end)
})
} else {
requestAnimationFrame(() => {
this.tableData = this.saveDATA.slice(this.starts, this.ends)
this.tableWarp.style.transform = `translateY(0px)`
if (this.fixLeft) {
this.fixLeft.style.transform = `translateY(0px)`
}
if (this.fixRight) {
this.fixRight.style.transform = `translateY(0px)`
}
})
}
}
},
created() {
// 是否開啟虛擬列表
if (this.useVirtual) {
this.init()
}
},
mounted() {
// 是否開啟虛擬列表
if (this.useVirtual) {
this.initMounted()
}
},
methods: {
initMounted() {
this.$nextTick(() => {
// 設(shè)置了滾動的盒子
this.tableRef = this.$refs['el-table'].bodyWrapper
// 左側(cè)固定列所在的盒子
this.tableFixedLeft = document.querySelector(
'.el-table .el-table__fixed .el-table__fixed-body-wrapper'
)
// 右側(cè)固定列所在的盒子
this.tableFixedRight = document.querySelector(
'.el-table .el-table__fixed-right .el-table__fixed-body-wrapper'
)
/**
* fixed-left | 主體 | fixed-right
*/
// 創(chuàng)建內(nèi)容盒子divWarpPar并且高度設(shè)置為所有數(shù)據(jù)所需要的總高度
let divWarpPar = document.createElement('div')
// 如果這里還沒獲取到saveDATA數(shù)據(jù)就渲染會導(dǎo)致內(nèi)容盒子高度為0,可以通過監(jiān)聽saveDATA的長度后再設(shè)置一次高度
divWarpPar.style.height = this.saveDATA.length * this.itemHeight + 'px'
// 新創(chuàng)建的盒子divWarpChild
let divWarpChild = document.createElement('div')
divWarpChild.className = 'fix-warp'
// 把tableRef的第一個子元素移動到新創(chuàng)建的盒子divWarpChild中
divWarpChild.append(this.tableRef.children[0])
// 把divWarpChild添加到divWarpPar中,最把divWarpPar添加到tableRef中
divWarpPar.append(divWarpChild)
this.tableRef.append(divWarpPar)
// left改造
let divLeftPar = document.createElement('div')
divLeftPar.style.height = this.saveDATA.length * this.itemHeight + 'px'
let divLeftChild = document.createElement('div')
divLeftChild.className = 'fix-left'
this.tableFixedLeft &&
divLeftChild.append(this.tableFixedLeft.children[0])
divLeftPar.append(divLeftChild)
this.tableFixedLeft && this.tableFixedLeft.append(divLeftPar)
// right改造
let divRightPar = document.createElement('div')
divRightPar.style.height = this.saveDATA.length * this.itemHeight + 'px'
let divRightChild = document.createElement('div')
divRightChild.className = 'fix-right'
this.tableFixedRight &&
divRightChild.append(this.tableFixedRight.children[0])
divRightPar.append(divRightChild)
this.tableFixedRight && this.tableFixedRight.append(divRightPar)
// 被設(shè)置的transform元素
this.tableWarp = document.querySelector(
'.el-table .el-table__body-wrapper .fix-warp'
)
this.fixLeft = document.querySelector(
'.el-table .el-table__fixed .el-table__fixed-body-wrapper .fix-left'
)
this.fixRight = document.querySelector(
'.el-table .el-table__fixed-right .el-table__fixed-body-wrapper .fix-right'
)
this.tableRef.addEventListener('scroll', this.onScroll)
})
},
// 初始化數(shù)據(jù)
init() {
this.saveDATA = this.table?.data
this.tableData = this.saveDATA.slice(this.start, this.end)
},
// 滾動事件
onScroll() {
this.scrollTop = this.tableRef.scrollTop
this.scrollNum = Math.floor(this.scrollTop / (this.itemHeight * this.pageList))
}
}
}
</script>
六、源碼地址
GitHub源碼地址
Gitee源碼地址
基于ElementUi或Antd再次封裝基礎(chǔ)組件文檔文章來源:http://www.zghlxwxcb.cn/news/detail-695002.html
vue3+ts基于Element-plus再次封裝基礎(chǔ)組件文檔文章來源地址http://www.zghlxwxcb.cn/news/detail-695002.html
到了這里,關(guān)于vue+element-ui el-table組件二次封裝實現(xiàn)虛擬滾動,解決數(shù)據(jù)量大渲染DOM過多而卡頓問題的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!