溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

基于Vue如何封裝一個(gè)虛擬列表組件

發(fā)布時(shí)間:2023-03-07 10:26:22 來源:億速云 閱讀:107 作者:iii 欄目:開發(fā)技術(shù)

今天小編給大家分享一下基于Vue如何封裝一個(gè)虛擬列表組件的相關(guān)知識(shí)點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識(shí),所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

    組件效果

    基于Vue如何封裝一個(gè)虛擬列表組件

    使用方法

    <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)行操作增刪等

    滾動(dòng)條正常顯示

    需要把顯示框分為3部分:顯示高度,全部高度,虛擬數(shù)據(jù)高度

    大概的比例是這樣的

    基于Vue如何封裝一個(gè)虛擬列表組件

    為達(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>

    加載渲染大量數(shù)據(jù)不卡頓

    如果想要渲染不卡頓,就得只加載顯示區(qū)域的虛擬數(shù)據(jù),虛擬數(shù)據(jù)的更新邏輯為:用startIndexendIndex標(biāo)志虛擬數(shù)據(jù)的起始索引和結(jié)束索引,在滾動(dòng)條滑動(dòng)時(shí),通過計(jì)算滑動(dòng)的距離去更新startIndexendIndex。另外用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)
    }

    能對(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" :/>
      <!-- 存放顯示數(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è)資訊頻道。

    向AI問一下細(xì)節(jié)

    免責(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)容。

    vue
    AI