以級(jí)聯(lián)選擇器為例,聊聊數(shù)據(jù)量過(guò)大造成的頁(yè)面卡頓問(wèn)題
背景
?列表、樹(shù)、級(jí)聯(lián)選擇都是用來(lái)展示“大量”、“相似度高”的數(shù)據(jù),它們都是需要在dom中生成大量的節(jié)點(diǎn),這就造成了數(shù)據(jù)量大造成頁(yè)面卡頓的原因,當(dāng)然以elementui為例,其實(shí)el-tree和el-cascader都對(duì)外提供了允許數(shù)據(jù)懶加載提供了接口。還是以級(jí)聯(lián)選擇器為例,僅僅是提供了父節(jié)點(diǎn)到子節(jié)點(diǎn)的懶加載,若子節(jié)點(diǎn)數(shù)據(jù)量大,那就的需要我們自己來(lái)二次封裝。下面就來(lái)簡(jiǎn)單實(shí)現(xiàn)一下這個(gè)二次封裝的懶加載。下面實(shí)現(xiàn)的組件僅僅考慮子節(jié)點(diǎn)數(shù)據(jù)量大的情況
?
效果展示

考慮因素
??
我們暫時(shí)稱(chēng)一級(jí)菜單為「類(lèi)型」,二級(jí)菜單為「標(biāo)簽」 初始化時(shí),我們?cè)O(shè)置一個(gè)固定的標(biāo)簽個(gè)數(shù)n,如果每個(gè)類(lèi)型大于等于n,則顯示個(gè)數(shù)為n,如果小于n則顯示已有的個(gè)數(shù) 當(dāng)我們滾動(dòng)條滾動(dòng)到底部時(shí),繼續(xù)加載n個(gè)標(biāo)簽,直到加載完畢。 加載完新的數(shù)據(jù)之后,要讓滾動(dòng)條停留到上次滾動(dòng)的位置 假如我們?cè)O(shè)置了默認(rèn)全選,則要在懶加載的時(shí)候,加載出來(lái)的新標(biāo)簽也要設(shè)置為選擇狀態(tài) 盡管首次渲染僅僅加載了部分標(biāo)簽,但是允許搜索的時(shí)候,范圍為所有的標(biāo)簽數(shù)據(jù) 全選的時(shí)候要有一個(gè)標(biāo)識(shí)來(lái)告訴用戶(hù)現(xiàn)在是全選的狀態(tài)
關(guān)鍵代碼
在觸發(fā)每個(gè)「類(lèi)型」的click時(shí)候,需要為級(jí)聯(lián)面板綁定滾動(dòng)監(jiān)聽(tīng),并將當(dāng)前的類(lèi)型傳到滾動(dòng)回調(diào)。當(dāng)然因?yàn)榛卣{(diào)函數(shù)為同一個(gè),所以不會(huì)造成多次監(jiān)聽(tīng)的問(wèn)題。
handleChange(value) {
if (!this.lazy) {
return
}
this.$nextTick(() => {
const panel = document.querySelectorAll('.el-cascader-menu__wrap.el-scrollbar__wrap')
const length = panel.length
const currentPanel = panel[length-1]
this.type = value[0]
if (currentPanel) {
currentPanel.addEventListener('scroll', this.scrollCallback(currentPanel))
}
})
},
大部分的邏輯都在滾動(dòng)回調(diào)中
scrollCallback(el) {
return () => {
const scrollTop = el.scrollTop
const clientHeight = el.clientHeight
const scrollHeight = el.scrollHeight
// 若scrollTop + clientHeight === scrollHeight 則說(shuō)明滾動(dòng)條到達(dá)了底部
if (scrollTop + clientHeight === scrollHeight) {
const tmp = []
// 當(dāng)前類(lèi)型下所有的選項(xiàng)
const allOptions = this.allOptions.filter(options=>options.value === this.type)[0]?.children
this.options.forEach(element => {
if (element.value === this.type) {
const optionLen = element.children.length-1
const allOptionsLen = allOptions.length-1
if (optionLen >= allOptionsLen) {
return
}
const loadsize = optionLen + this.loadSize
// 當(dāng)我們滾動(dòng)條滾動(dòng)到底部時(shí),繼續(xù)加載n個(gè)標(biāo)簽,直到加載完畢
const size = loadsize > allOptionsLen ? allOptionsLen : loadsize
for (var i=optionLen; i<=size; i++) {
tmp.push(allOptions[i])
if (this.isAllSelected) {
this.values.push([this.type, allOptions[i].value])
}
}
element.children.push(...tmp)
}
})
//加載完新的數(shù)據(jù)之后,要讓滾動(dòng)條停留到上次滾動(dòng)的位置
setTimeout(() => {
el.scrollTop = scrollTop
})
}
}
}
源碼與使用方式
?
源碼 npm install cu-vue-cascader import CuVueCascader from 'cu-vue-cascader' vue.use(CuVueCascader) ?// 幾個(gè)重要的參數(shù)
allOptions: array 所有的餐素
selectAll: boolean 默認(rèn)是否全選
allSelectedClass: {} 全選時(shí)候的樣式
lazy: boolean 是否開(kāi)啟子選項(xiàng)的懶加載
loadSize: number 子選擇每次加載的條數(shù)
評(píng)論
圖片
表情
