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條,滑動有延遲了,數據被節流了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM