您好,登錄后才能下訂單哦!
今天小編給大家分享一下基于Vue如何封裝一個(gè)虛擬列表組件的相關(guān)知識(shí)點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識(shí),所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
使用方法
<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ù) | 說明 | 類型 | 可選值 | 默認(rèn)值 |
---|---|---|---|---|
allData | 全部數(shù)據(jù) | Array | - | [] |
virtualData | 虛擬數(shù)據(jù) | Array | - | [] |
itemHeight | 每行的高度,用于計(jì)算滾動(dòng)距離 | Number, String | - | 30 |
插槽名 | 說明 |
---|---|
- | 自定義默認(rèn)內(nèi)容,即主體區(qū)域 |
首先梳理我想要的組件效果:
滾動(dòng)條正常顯示
加載渲染大量數(shù)據(jù)不卡頓
能對(duì)列表數(shù)據(jù)進(jìn)行操作增刪等
需要把顯示框分為3部分:顯示高度,全部高度,虛擬數(shù)據(jù)高度
大概的比例是這樣的
為達(dá)到滾動(dòng)條的效果,在最外層顯示高度設(shè)置overflow: auto
可以把滾動(dòng)條撐出來,全部高度則設(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" :/> <!-- 存放顯示數(shù)據(jù) --> <div class="virtual-list" :/> </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>
如果想要渲染不卡頓,就得只加載顯示區(qū)域的虛擬數(shù)據(jù),虛擬數(shù)據(jù)的更新邏輯為:用startIndex
和endIndex
標(biāo)志虛擬數(shù)據(jù)的起始索引和結(jié)束索引,在滾動(dòng)條滑動(dòng)時(shí),通過計(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é)束索引 = 開始索引 + 可見數(shù)量 this.endIndex = this.startIndex + this.visibleCount // 偏移量 = 滾動(dòng)距離 this.offset = scrollTop - (scrollTop % this.itemHeight) }
如果想要在數(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" :/> <!-- 存放顯示數(shù)據(jù) --> <div class="virtual-list" :> <!-- 設(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é)束索引 = 開始索引 + 可見數(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" :/> <!-- 存放顯示數(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 }, // 可見數(shù)量 visibleCount() { // 可見高度 / 每項(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é)束索引 = 開始索引 + 可見數(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>
以上就是“基于Vue如何封裝一個(gè)虛擬列表組件”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會(huì)為大家更新不同的知識(shí),如果還想學(xué)習(xí)更多的知識(shí),請(qǐng)關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。