123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- <template>
- <view style="flex: 1; background-color: aliceblue">
- <view class="tips">list-view组件虽然在渲染层有recycle机制,但长列表的vnode/Element太多也会造成初始化卡顿。
- 本示例有2000条数据,通过分批创建列表项,减少初始化卡顿。并通过闲时创建机制避免影响列表滚动等UI操作</view>
- <list-view style="flex: 1" :refresher-enabled="true" :refresher-triggered="refresherTriggered" @scroll="onScroll"
- @scrolltolower="onScrollToLower" @refresherrefresh="onRefresh" @refresherpulling="onRefresherPulling" @refresherrestore="onRefresherRestore">
- <list-item class="item" v-for="(item, index) in list" :key="index + '_' + item.id">
- <view><text>{{ item.name }}</text></view>
- <view><text style="font-size: 12px; color: #999999">{{
- item.info
- }}</text></view>
- </list-item>
- </list-view>
- </view>
- </template>
- <script lang="ts">
- type Item = {
- id : number;
- name : string;
- info : string;
- };
- class Jobs {
- private jobs : (() => Promise<void>)[] = [];
- paused : boolean = true;
- constructor() { }
- add(job : () => Promise<void>) {
- this.jobs.push(job);
- }
- pause() {
- this.paused = true;
- }
- private execute() {
- if (this.paused) {
- return;
- }
- if (this.jobs.length == 0) {
- this.paused = true
- return;
- }
- const job = this.jobs.shift();
- if (job != null) {
- job().then(() => {
- this.execute();
- });
- }
- }
- resume() {
- if(!this.paused) {
- return
- }
- this.paused = false;
- setTimeout(() => {
- this.execute();
- }, 0)
- }
- reset() {
- this.jobs = [];
- this.paused = true;
- }
- get done() : boolean {
- return this.jobs.length == 0;
- }
- }
- function delay(time : number) : Promise<void> {
- return new Promise(resolve => {
- setTimeout(() => {
- resolve()
- }, time)
- })
- }
- export default {
- data() {
- return {
- bigList: [] as Item[],
- list: [] as Item[],
- jobs: new Jobs(),
- batchSize: 100,
- scrolling: false,
- refresherTriggered: false,
- scrollendTimeout: -1,
- pulling: false,
- };
- },
- created() {
- // 模拟大列表数据
- for (let i = 0; i < 2000; i++) {
- this.bigList.push({
- id: i,
- name: `Wifi_` + i,
- info: `信号强度: -${Math.floor(Math.random() * 60) + 40
- } db, 安全性: WPA/WPA2/WPA3-Personal`,
- } as Item);
- }
- },
- onReady() {
- this.init();
- },
- methods: {
- init(autoResumeJobs: boolean = true) {
- // 将数据分批放入任务队列
- const batchCount = Math.ceil(this.bigList.length / this.batchSize);
- for (let i = 0; i < batchCount; i++) {
- const start = i * this.batchSize;
- const end = Math.min(start + this.batchSize, this.bigList.length);
- this.jobs.add(async () => {
- this.list.push(...this.bigList.slice(start, end));
- // 两批数据之间增加延迟,防止卡顿时间太久
- await this.$nextTick();
- await delay(100)
- });
- }
- if(autoResumeJobs) {
- this.jobs.resume();
- }
- },
- onScroll() {
- // 部分平台不支持scrollend事件,使用定时器模拟
- clearTimeout(this.scrollendTimeout)
- this.scrollendTimeout = setTimeout(() => {
- this.onScrollEnd()
- }, 100)
- if (this.scrolling) {
- return;
- }
- this.scrolling = true;
- // 滚动期间暂停分批加载,保证滚动流畅度
- this.jobs.pause();
- },
- onScrollEnd() {
- this.scrolling = false;
- // 滚动结束,继续执行分批加载逻辑
- this.jobs.resume();
- },
- onScrollToLower() {
- // 分批加载进行中,暂不执行滚动到底部加载更多数据的逻辑
- if (!this.jobs.done) {
- return;
- }
- // 加载更多数据
- },
- onRefresh() {
- // 下拉刷新触发,重置任务队列
- this.jobs.reset();
- this.refresherTriggered = true
- setTimeout(() => {
- this.refresherTriggered = false;
- this.list.splice(0, this.list.length);
- /**
- * refreshRestore事件会触发继续分批加载,此处init不自动调用resume。这样做能减少一些下拉刷新复位期间加载数据引发的卡顿。
- * TODO 清空列表也会导致下拉刷新复位时发生卡顿,后续再优化
- */
- this.init(false);
- }, 500)
- },
- onRefresherPulling() {
- if(this.pulling) {
- return
- }
- this.pulling = true
- // 在下拉刷新时暂停分批加载,避免影响刷新操作
- this.jobs.pause()
- },
- onRefresherRestore() {
- this.pulling = false
- // 下拉刷新结束后恢复分批加载
- this.jobs.resume();
- }
- },
- };
- </script>
- <style>
- .tips {
- margin: 10px;
- border-radius: 5px;
- padding: 10px;
- background-color: white;
- }
- .item {
- margin: 5px 10px;
- padding: 10px;
- border-radius: 5px;
- background-color: white;
- }
- </style>
|