uniapp长列表虚拟加载实例(前端处理上万条数据加载优化,不卡,纯手敲)

//temple部分

<template>
  <!-- 本页面是虚拟加载组件,还有个长列表懒加载组件(sickListPageLoad.vue)。 -->
  <view class="sickBody">
    <scroll-view scroll-y="true" class="scroll-Y" @scroll="scroll">
      <view class="parentDom">
        <!-- <view :style="{ height: sickAllList.length * 40 + 'px' }"></view> -->
        <view :style="{ height: screenHeight + 'px' }"></view>
        <view class="positionRelative" :style="{ transform: getTransform }">
          <view v-for="item in visibleData" :key="item.id" class="height40" @click="choseSick(item)">{{
            item.name
          }}</view>
        </view>
      </view>
    </scroll-view>
  </view>
</template>

js部分

let testdata = [];
for (let i = 0; i < 300; i++) {
  testdata.push({ id: i, name: '疾病' + i });
}
/**
 * 搜索组件
 */
import { throttle } from '@/utils/optimize'; //防抖,自己写一个
export default {
  props: {
    // 是否展示搜索按钮
    listData: {
      type: Array,
      default: () => []
    },
    itemSize: {
      type: Number,
      default: 40
    }
  },
  data() {
    return {
      searchList: [],
      // screenHeight: 1200,
      startOffset: 0,
      start: 0,
      end: 20,
      scrollTData: 0,
      count: 20,
      remain: 8
      // testData: []
    };
  },
  computed: {
    listHeight() {
      return testdata.length * this.itemSize;
    },
    screenHeight() {
      return testdata.length * Number(this.itemSize) + 100;
    },
    // 前面预留几个
    prevCount() {
      return Math.min(this.start, this.remain);
    },
    // 后面预留几个
    nextCount() {
      return Math.min(this.remain, this.end);
    },
    getTransform() {
      return `translate3d(0,${this.startOffset}px,0)`;
    },
    visibleData() {
      return testdata.slice(this.start, Math.min(this.end, testdata.length));
    }
  },
  mounted() {
    this.sickAllList = testdata;
  },
  methods: {
    close() {
      this.$emit('popClose');
    },
    center() {
      this.$emit('popCenter');
    },
    go_search() {
      this.$wxPromise.navigateTo({
        url: '/pages/searchDrug/index'
      });
    },
    choseSick(item) {
      this.$emit('choseSickSearch', item);
    },
    scroll(e) {
      this.scrollTData = e.target.scrollTop;
      this.scrollThrottle();
    },
    /* eslint-disable */
    scrollThrottle: throttle(function () {
      // uni.showLoading({
      //   title: '加载中',
      //   mask: true
      // });
      let scrollTop = this.scrollTData; // e.target.scrollTop;
      // 此时的开始索引
      this.start =
        Math.floor(scrollTop / this.itemSize) - this.prevCount >= 0
          ? Math.floor(scrollTop / this.itemSize) - this.prevCount
          : 0;
      // 此时的结束索引
      this.end = Number(this.start) + Number(this.count) + Number(this.nextCount);
      // 此时的偏移量
      // console.log('位置', this.start, this.end);
      this.startOffset = Number(this.start) * Number(this.itemSize);
    }, 0)
  }
};

样式

<style lang="scss">
.sickBody {
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background: #f4f4f4;
  z-index: 99;
}
.infinite-list-container {
  height: 100%;
  overflow: auto;
  position: relative;
  -webkit-overflow-scrolling: touch;
}

.infinite-list-phantom {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  z-index: -1;
}

.infinite-list {
  left: 0;
  right: 0;
  top: 0;
  position: absolute;
  text-align: center;
}

.infinite-list-item {
  padding: 10px;
  color: #555;
  box-sizing: border-box;
  border-bottom: 1px solid #999;
}
.scroll-Y {
  height: 100%;
  overflow-y: scroll;
}
.height40 {
  height: 39px;
  line-height: 39px;
  width: 100%;
  overflow: hidden;
  border-bottom: 1px solid #d2d2d2;
}
.positionRelative {
  width: 92%;
  margin: 0 3%;
  position: absolute;
  left: 0;
  top: 0;
  font-size: 32rpx;
  padding-bottom: 300rpx;
  //   height: 100%;
  //   width: 100%;
}
.parentDom {
  position: relative;
}
</style>

总结

总结:

思路很简单:

一、 拿到所有数据应该占用的高度。比如1万条数据,每条占40px,占用高度应为1万*40;

二、拿到展示区域的高度,比如我想展示50条数据,展示高度即为50*40;

三、拿到屏幕滚动的距离,用滚动的距离/行高,获取当前第一行应该展示对应数据的下标值,比如滚动了400px,当前展示的第一条数据的下标应为10

四、截取需要展示的数据:this.testData.slice(this.start, Math.min(this.end, this.testData.length)); 注意前后预留数据,要不然会有空白出现

五、完工

注意事项:一定要脱离标准文档流,给展示区域加定位position:absolute。要不然回流会让你电脑崩溃。里面要不要加节流防抖等细节可以自己看着加。我初期尝试加了节流,加了反而可能造成数据更新会缺少n条,滑动有延迟了,数据被节流了。

posted @ 2021-03-05 11:08  问问大将军  阅读(5474)  评论(4编辑  收藏  举报