第一节:vue长列表处理

374 阅读1分钟

有时候数据过大,会导致页面渲染过慢占用cpu过多,所以需要对数据进行处理

处理方式就是在固定的高度的容器中展示可见的数据,通过控制滚动条来修改数据

<template>
  <div class="home">
    <RecycleScrollerVue 
    :items="items" 
    :item-size="54" 
    class="scroller"
    v-slot="{item}"
    >
      <ListItemVue :item="item"></ListItemVue>
    </RecycleScrollerVue>
  </div>
</template>

<script>
// import Simple from '../components/drawer/simple.vue'
import ListItemVue from "./ListItem.vue"
import RecycleScrollerVue from "./RecycleScroller.vue"

var items = [];
for(let i = 0; i < 10000; i++) {
  //=> 添加10000条数据
  items.push({
    id: i + 1,
    count: i + 1
  })
}
export default {
  name: 'Home',
  components: {
    ListItemVue,
    RecycleScrollerVue
  },
  data() {
    return {
      items
    }
  }
}
</script>

<style scoped>
.scroller {
  width: 600px;
  margin: 0 auto;
  height: 500px;
}
</style>
<template>
  <div class="list-item">
    <span>id{{ item.id }}</span>
    <span>count{{ item.count }}</span>
    <span>age{{ item.count }}</span>
  </div>
</template>

<script>
export default {
  props: {
    item: Object
  }
}
</script>

<style>
.list-item {
  text-align: center;
  height: 54px;
  padding: 1rem;
  box-sizing: border-box;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
}
</style>
<template>
  <div class="recycle-scroller-container" @scroll="setPool" ref="container">
    <div class="recycle-scroller-wrapper" :style="{height: `${totalSize}px`}">
      <div 
      class="recycle-scroller-item" 
      v-for="poolItem in pool" 
      :key="poolItem[keyField]"
      :style="{transform: `translateY(${poolItem.position}px)`}"
      >
        <!-- 作用域插槽 -->
        <slot :item="poolItem.item"></slot>
      </div>
    </div>
  </div>
</template>

<script>
// 防止滑动过快出现白板,前面多显示10个,后面也多显示10个(也可配置props)
let prev = 10,
    next = 10;

//可回收的使用
export default {
  name: 'RecycleScroller',
  props: {
    items: {
      type: Array,
      default: () => []
    },
    // 每条数据的高度
    itemSize: {
      type: Number,
      default: 0
    },
    keyField: {//以什么作为key值
      type: String,
      default: 'id'
    }
  },
  mounted() {
    // 设置数据池
    this.setPool();
    window.vm = this;
  },
  data() {
    return {
      //{item: 原始数据, position: 该数据的偏移量, }
      pool: [],//渲染池,保存当前需要渲染的数据
    }
  },
  computed: {
    totalSize() {
      return this.items.length * this.itemSize;//计算总高度
    }
  },
  methods: {
    setPool() {
      // 获取滚动高度,计算前面有多少项不可见
      const scrollTop = this.$refs.container.scrollTop
      const conHeight = this.$refs.container.clientHeight//容器的高度
      console.log(scrollTop)
      // 开始下标以及结束下标
      let startIndex = Math.floor(scrollTop / this.itemSize)//向下取整
      let endIndex = Math.ceil((scrollTop + conHeight)/this.itemSize);//(滚动高度 + 容器高度)/每个子元素的高度

      startIndex -= prev;
      if(startIndex < 0) {
        startIndex = 0
      }
      endIndex += next//不用处理了因为slice的截取超过了也没用
      const startPos = startIndex * this.itemSize//起始下标偏移量
      this.pool = this.items.slice(startIndex, endIndex).map((item, i) => ({
        item,
        position: startPos + i * this.itemSize
      }));
    }
  }
}
</script>

<style>
.recycle-scroller-container {
  /* 高度不知道不去设置 */
  overflow: auto;
}

.recycle-scroller-wrapper {
  position: relative;
}

.recycle-scroller-item {
  position: absolute;
  width: 100%;
  left: 0;
  top: 0;
}
</style>

效果图如下:

image.png

image.png