数据懒加载

43 阅读1分钟

1. 有高度

1.1 vue-infinite-loading(vue2)

<infinite-loading
  slot="append"
  :identifier="identifier"
  @infinite="loadManageTable"
  force-use-infinite-wrapper=".el-table__body-wrapper"
>
  <span slot="no-more"></span>
</infinite-loading>

// js
import InfiniteLoading from 'vue-infinite-loading'

identifier: new Date(),

loadManageTable($state) {
  setTimeout(() => {
    if (this.list.length && this.list.length >= this.listInit.length) {
      $state.complete()
      return
    }
    this.list.push(
      ...this.listInit.slice(
        this.list.length,
        this.list.length + this.loadingPageSize
      )
    )
    this.list.length && $state.loaded()
  }, 500)
},
deactivated() {
  this.identifier = new Date()
},
destroyed() {
  this.identifier = null
}

具体查看

2. 剖析原理

<template>
  <div class="vitual-list-wrap" ref="listWrapRef">
    <div class="content" :style="contentStyle">
      <div class="item" v-for="(item, index) in viewData.list" :key="index" :style="item.style">
        {{ item.content }}
      </div>
    </div>
  </div>
</template>

<script setup>
/* eslint-disable */

import { createApp, reactive, toRefs, computed, onMounted, ref } from 'vue'
const listWrapRef = ref(null)
const viewData = reactive({
  list: [],
  total: 1000, // 数据总条数
  height: 600, // 可视区域的高度
  rowHeight: 60, // 每条item的高度
  startIndex: 0, // 初始位置
  endIndex: 0, // 结束位置
  timer: false,
  bufferSize: 5 // 做一个缓冲
})
const contentStyle = computed(() => {
  return {
    height: `${viewData.total * viewData.rowHeight}px`,
    position: 'relative'
  }
})
// todo 设置数据
const renderData = () => {
  viewData.list = []
  const { rowHeight, height, startIndex, total, bufferSize } = viewData
  // 当前可视区域的row条数
  const limit = Math.ceil(height / rowHeight)
  console.log(limit, '=limit')
  // 可视区域的最后一个位置
  viewData.endIndex = Math.min(startIndex + limit + bufferSize, total - 1)
  for (let i = startIndex; i < viewData.endIndex; i++) {
    viewData.list.push({
      content: i,
      style: {
        top: `${i * rowHeight}px`
      }
    })
  }
}
// todo 监听滚动,设置statIndex与endIndex
const handleScroll = callback => {
  listWrapRef.value &&
    listWrapRef.value.addEventListener('scroll', e => {
      if (viewData.timer) {
        return
      }
      const { rowHeight, startIndex, bufferSize } = viewData
      const { scrollTop } = e.target
      console.log('scrollTop: ', scrollTop)
      // 计算当前滚动的位置,获取当前开始的起始位置
      const currentIndex = Math.floor(scrollTop / rowHeight)
      viewData.timer = true
      console.log(startIndex, currentIndex)
      // 做一个简单的节流处理
      setTimeout(() => {
        viewData.timer = false
        // 如果滑动的位置不是当前位置
        if (currentIndex !== startIndex) {
          viewData.startIndex = Math.max(currentIndex - bufferSize, 0)
          callback()
        }
      }, 500)
    })
}
onMounted(() => {
  renderData()
  handleScroll(renderData)
})
</script>

<style scoped>
* {
  padding: 0px;
  margin: 0px;
}
#app {
  width: 300px;
  border: 1px solid#e5e5e5;
}
/*外部容器给一个固定的可视高度,并且设置可以滚动*/
.vitual-list-wrap {
  position: relative;
  height: 800px;
  overflow-y: auto;
  background-color: #ccc;
}
/*真实容器的区域*/
.content {
  position: relative;
}
/*固定高度的每个元素*/
.item {
  height: 60px;
  padding: 10px 5px;
  border-bottom: 1px solid#111;
  position: absolute;
  left: 0;
  right: 0;
  line-height: 60px;
}
</style>