基于Vue實(shí)現(xiàn)封裝一個(gè)虛擬列表組件
正常情況下,我們對(duì)于數(shù)據(jù)都會(huì)分頁(yè)加載,最近項(xiàng)目中確實(shí)遇到了不能分頁(yè)的場(chǎng)景,如果不分頁(yè),頁(yè)面渲染幾千條數(shù)據(jù)就會(huì)感知到卡頓,使用虛擬列表就勢(shì)在必行了,為了增加復(fù)用性,封裝成了組件。
組件效果
使用方法
<template> <div> <div class="virtual-list-md-wrap"> <hub-virtual-list :allData="data" itemHeight="70" :virtualData.sync="virtualData"> <div v-for="(item, index) in virtualData" class="item"> {{ item }} <el-button type="primary" size="mini" plain @click="deleteItem(item)">刪除</el-button> </div> </hub-virtual-list> </div> </div> </template> <script> export default { data() { return { data: [], virtualData: [] } }, created() { setTimeout(() => { this.addData() }, 1000) }, watch: { }, methods: { addData() { for(let i = 0; i <= 100000; i ++) { this.$set(this.data, i, i) } }, deleteItem(index) { this.data = this.data.filter((item) => item !== index) } } } </script> <style> .virtual-list-md-wrap { height: 500px; background-color: #FFFAF0; } .item { border-bottom: 1px solid #666; padding: 20px; text-align: center; } </style>
屬性
參數(shù) | 說(shuō)明 | 類型 | 可選值 | 默認(rèn)值 |
---|---|---|---|---|
allData | 全部數(shù)據(jù) | Array | - | [] |
virtualData | 虛擬數(shù)據(jù) | Array | - | [] |
itemHeight | 每行的高度,用于計(jì)算滾動(dòng)距離 | Number, String | - | 30 |
插槽
插槽名 | 說(shuō)明 |
---|---|
- | 自定義默認(rèn)內(nèi)容,即主體區(qū)域 |
封裝過(guò)程
首先梳理我想要的組件效果:
- 滾動(dòng)條正常顯示
- 加載渲染大量數(shù)據(jù)不卡頓
- 能對(duì)列表數(shù)據(jù)進(jìn)行操作增刪等
滾動(dòng)條正常顯示
需要把顯示框分為3部分:顯示高度,全部高度,虛擬數(shù)據(jù)高度
大概的比例是這樣的
為達(dá)到滾動(dòng)條的效果,在最外層顯示高度設(shè)置overflow: auto
可以把滾動(dòng)條撐出來(lái),全部高度則設(shè)置position: absolute;z-index: -1;height: auto;
,虛擬數(shù)據(jù)高度則設(shè)置position: absolute; height: auto;
整體樣式代碼如下
<template> <div class="hub-virtual-list"> <!-- 顯示高度 --> <div ref="virtualList" class="hub-virtual-list-show-height" @scroll="scrollEvent($event)"> <!-- 全部高度,撐出滾動(dòng)條 --> <div class="hub-virtual-list-all-height" :style="{height: allHeight + 'px'}"/> <!-- 存放顯示數(shù)據(jù) --> <div class="virtual-list" :style="{ transform: getTransform }"/> </div> </div> </template> <style lang="scss" scoped> .hub-virtual-list { height: 100%; &-show-height { position: relative; overflow: auto; height: 100%; -webkit-overflow-scrolling: touch; } &-all-height { position: absolute; left: 0; top: 0; right: 0; z-index: -1; height: auto; } .virtual-list { position: absolute; left: 0; top: 0; right: 0; height: auto; } } </style>
加載渲染大量數(shù)據(jù)不卡頓
如果想要渲染不卡頓,就得只加載顯示區(qū)域的虛擬數(shù)據(jù),虛擬數(shù)據(jù)的更新邏輯為:用startIndex
和endIndex
標(biāo)志虛擬數(shù)據(jù)的起始索引和結(jié)束索引,在滾動(dòng)條滑動(dòng)時(shí),通過(guò)計(jì)算滑動(dòng)的距離去更新startIndex
和endIndex
。另外用offset
標(biāo)記偏移量,對(duì)虛擬數(shù)據(jù)區(qū)域設(shè)置transform: translate3d(0, ${this.offset}px, 0)
跟著滾動(dòng)條去移動(dòng)
核心部分代碼如下
scrollEvent(e) { const scrollTop = this.$refs.virtualList.scrollTop // 起始索引 = 滾動(dòng)距離 / 每項(xiàng)高度 this.startIndex = Math.floor(scrollTop / this.itemHeight) // 結(jié)束索引 = 開(kāi)始索引 + 可見(jiàn)數(shù)量 this.endIndex = this.startIndex + this.visibleCount // 偏移量 = 滾動(dòng)距離 this.offset = scrollTop - (scrollTop % this.itemHeight) }
能對(duì)列表數(shù)據(jù)進(jìn)行操作增刪等
如果想要在數(shù)據(jù)里添加操作按鈕,則需要在封裝組件時(shí)設(shè)置插槽,且需要把虛擬數(shù)據(jù)同步給父組件
設(shè)置插槽
<!-- 顯示高度 --> <div ref="virtualList" class="hub-virtual-list-show-height" @scroll="scrollEvent($event)"> <!-- 全部高度,撐出滾動(dòng)條 --> <div class="hub-virtual-list-all-height" :style="{height: allHeight + 'px'}"/> <!-- 存放顯示數(shù)據(jù) --> <div class="virtual-list" :style="{ transform: getTransform }"> <!-- 設(shè)置插槽 --> <slot/> </div> </div>
滾動(dòng)時(shí)把虛擬數(shù)據(jù)同步給父組件
scrollEvent(e) { const scrollTop = this.$refs.virtualList.scrollTop // 起始索引 = 滾動(dòng)距離 / 每項(xiàng)高度 this.startIndex = Math.floor(scrollTop / this.itemHeight) // 結(jié)束索引 = 開(kāi)始索引 + 可見(jiàn)數(shù)量 this.endIndex = this.startIndex + this.visibleCount // 偏移量 = 滾動(dòng)距離 this.offset = scrollTop - (scrollTop % this.itemHeight) // 同步父組件數(shù)據(jù) this.inVirtualData = this.allData.slice(this.startIndex, this.endIndex) this.$emit('update:virtualData', this.inVirtualData) }
完整代碼
<template> <div class="hub-virtual-list"> <!-- 顯示高度 --> <div ref="virtualList" class="hub-virtual-list-show-height" @scroll="scrollEvent($event)"> <!-- 全部高度,撐出滾動(dòng)條 --> <div class="hub-virtual-list-all-height" :style="{height: allHeight + 'px'}"/> <!-- 存放顯示數(shù)據(jù) --> <div class="virtual-list" > <slot/> </div> </div> </div> </template> <script> export default { name: 'hub-virtual-list', props: { // 全部數(shù)據(jù) allData: { type: Array, default: () => [] }, // 虛擬數(shù)據(jù) virtualData: { type: Array, default: () => [] }, // 每項(xiàng)高度 itemHeight: { type: [Number, String], default: '30' }, // 每項(xiàng)樣式 itemStyle: { type: Object, default: () => {} } }, data() { return { // 起始索引 startIndex: 0, // 結(jié)束索引 endIndex: null, // 偏移量,計(jì)算滾動(dòng)條 offset: 0, inVirtualData: [] } }, computed: { // 所有高度 allHeight() { // 每項(xiàng)高度 * 項(xiàng)數(shù) return this.itemHeight * this.allData.length }, // 可見(jiàn)數(shù)量 visibleCount() { // 可見(jiàn)高度 / 每項(xiàng)高度 return Math.ceil(this.showHeight / this.itemHeight) }, // 顯示數(shù)據(jù)的偏移量 getTransform() { return `translate3d(0, ${this.offset}px, 0)` } }, watch: { allData: { handler() { this.inVirtualData = this.allData.slice(this.startIndex, this.endIndex) this.$emit('update:virtualData', this.inVirtualData) }, deep: true } }, mounted() { this.showHeight = this.$el.clientHeight this.startIndex = 0 this.endIndex = this.startIndex + this.visibleCount }, methods: { scrollEvent(e) { const scrollTop = this.$refs.virtualList.scrollTop // 起始索引 = 滾動(dòng)距離 / 每項(xiàng)高度 this.startIndex = Math.floor(scrollTop / this.itemHeight) // 結(jié)束索引 = 開(kāi)始索引 + 可見(jiàn)數(shù)量 this.endIndex = this.startIndex + this.visibleCount // 偏移量 = 滾動(dòng)距離 this.offset = scrollTop - (scrollTop % this.itemHeight) // 同步父組件數(shù)據(jù) this.inVirtualData = this.allData.slice(this.startIndex, this.endIndex) this.$emit('update:virtualData', this.inVirtualData) } } } </script> <style lang="scss" scoped> .hub-virtual-list { height: 100%; &-show-height { position: relative; overflow: auto; height: 100%; -webkit-overflow-scrolling: touch; } &-all-height { position: absolute; left: 0; top: 0; right: 0; z-index: -1; height: auto; } .virtual-list { position: absolute; left: 0; top: 0; right: 0; height: auto; } } </style>
待完善
使用組件時(shí)需要設(shè)置itemHeight
屬性才能去計(jì)算整體的高度去保證滾動(dòng)條的準(zhǔn)確性,并且itemHeight的值要和實(shí)際高度相等,不然會(huì)出現(xiàn)滾動(dòng)條滑到底部時(shí)出現(xiàn)空白的情況。同時(shí)組件僅支持每一行高度固定且相等的情況,對(duì)于每行數(shù)據(jù)高度不相等的情況目前還未完善。
以上就是基于Vue實(shí)現(xiàn)封裝一個(gè)虛擬列表組件的詳細(xì)內(nèi)容,更多關(guān)于Vue封裝虛擬列表組件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- vue實(shí)現(xiàn)不定高虛擬列表的示例詳解
- 不同場(chǎng)景下Vue中虛擬列表實(shí)現(xiàn)
- 詳解vue3中虛擬列表組件的實(shí)現(xiàn)
- Vue中虛擬列表的原理與實(shí)現(xiàn)詳解
- vue長(zhǎng)列表優(yōu)化之虛擬列表實(shí)現(xiàn)過(guò)程詳解
- 結(jié)合康熙選秀講解vue虛擬列表實(shí)現(xiàn)
- Vue 虛擬列表的實(shí)戰(zhàn)示例
- vue實(shí)現(xiàn)虛擬列表功能的代碼
- 使用 Vue 實(shí)現(xiàn)一個(gè)虛擬列表的方法
- vue簡(jiǎn)單實(shí)現(xiàn)一個(gè)虛擬列表的示例代碼
相關(guān)文章
基于vue cli重構(gòu)多頁(yè)面腳手架過(guò)程詳解
本文分步驟給大家介紹了基于vue cli重構(gòu)多頁(yè)面腳手架過(guò)程,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2018-01-01vue 的 solt 子組件過(guò)濾過(guò)程解析
這篇文章主要介紹了vue 的 solt 子組件過(guò)濾過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09vue如何利用axios調(diào)用后臺(tái)api接口
這篇文章主要介紹了vue如何利用axios調(diào)用后臺(tái)api接口問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07vue2文件流下載成功后文件格式錯(cuò)誤、打不開(kāi)及內(nèi)容缺失的解決方法
使用Vue時(shí)我們前端如何處理后端返回的文件流,下面這篇文章主要給大家介紹了關(guān)于vue2文件流下載成功后文件格式錯(cuò)誤、打不開(kāi)及內(nèi)容缺失的解決方法,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-04-04elementUI中el-table表頭和內(nèi)容全部一行顯示完整的方法
最近參與web開(kāi)發(fā)時(shí),讓我解決一個(gè)elementui控制內(nèi)容單行顯示,下面這篇文章主要給大家介紹了關(guān)于elementUI中el-table表頭和內(nèi)容全部一行顯示完整的方法,需要的朋友可以參考下2023-06-06VUE中的export default和export使用方法解析
export default和export都能導(dǎo)出一個(gè)模塊里面的常量,函數(shù),文件,模塊等,在其它文件或模塊中通過(guò)import來(lái)導(dǎo)入常量,函數(shù),文件或模塊。但是,在一個(gè)文件或模塊中export,import可以有多個(gè),export default卻只能有一個(gè)。2022-12-12vue 實(shí)現(xiàn)左右拖拽元素并且不超過(guò)他的父元素的寬度
這篇文章主要介紹了vue 實(shí)現(xiàn)左右拖拽元素并且不超過(guò)他的父元素的寬度,需要的朋友可以參考下2018-11-11Vue項(xiàng)目中使用WebUploader實(shí)現(xiàn)文件上傳的方法
WebUploader是由 Baidu WebFE(FEX) 團(tuán)隊(duì)開(kāi)發(fā)的一個(gè)簡(jiǎn)單的以 HTML5為主 , FLASH為輔 的現(xiàn)代 文件上傳組件 。這篇文章主要介紹了在Vue項(xiàng)目中使用WebUploader實(shí)現(xiàn)文件上傳,需要的朋友可以參考下2019-07-07