闽公网安备 35020302035485号

// 堆代码 duidaima.com
// dataList 就是我们整个的商品卡片列表的数据 ,用户滑动到底部会加载新一页的数据 会再次触发 watch
watch(() => props.dataList ,(newList) => {
dataRender(newList)
},{
immediate: true,
})
const dataRender = async (newList) => {
// 获取左右两边的高度
let leftHeight: number = await getViewHeight('#left')
let rightHeight: number = await getViewHeight('#right')
// 取下一页数据
const tempList = newVal.slice(lastIndex.value, newVal.length)
for await (const item of tempList) {
leftHeight <= rightHeight ? leftDataList.value.push(item) : rightDataList.value.push(item); //判断两边高度,来决定添加到那边
// 渲染dom
await nextTick();
// 获取dom渲染后的 左右两边的高度
leftHeight = await getViewHeight('#left')
rightHeight = await getViewHeight('#right')
}
lastIndex.value = newList.length
}
<template>
<view>
<view id="left">xxxx</view>
<view id="right">xxxx</view>
</view>
</template>
当用户滚动到底部的时候会加载下一页的数据,dataList 会发生变化,组件会监听到 dataList 的变化来执行 dataRender,dataRender 中会去计算左右两列的高度,哪边更短来插入哪边,循环 list 来完成整个列表的插入。// 正常情况代码会像如下情况去走 list = [1,2,3,4,5] // 数组执行完成后 lastIndex = 5 // 加载下一页数据后 list = [1,2,3,4,5,6,7,8,9,10] list.slice(lastIndex, list.length) // [6,7,8,9,10]但是如果 dataRender 同时执行 大家都共用同一个 lastIndex ,lastIndex 并不是最新的,就会变成下面这种情况
list.slice(lastIndex, list.length) // [1,2,3,4,5,6,7,8,9,10]同理顺序错乱也是这种情况
const fallLoad = ref(true)
watch(() => {
if(fallLoad.value) {
dataRender()
fallLoad.value = false
}
})
const dataRender = async () => {
let i = 0
const tempList = newVal.slice(lastIndex.value, newVal.length)
for await (const item of tempList) {
i++
leftHeight <= rightHeight ? leftDataList.value.push(item) : rightDataList.value.push(item); //判断两边高度,来决定添加到那边
// 等待dom渲染完成
await nextTick();
// 获取dom渲染后的 左右两边的高度
leftHeight = await getViewHeight('#left')
rightHeight = await getViewHeight('#right')
// 判断是最后一个节点
if((tempList.length - 1) === i) {
fallLoad.value = true
}
}
lastIndex.value = newList.length
}
这样的话会丢弃掉用户快速滑动时触发的 dataRender ,只有在 DOM 渲染完成后再次触发新的请求时才会再次触发。但是这样可能会存在另外一个问题,有部分的 dataRender 被丢弃掉了,同时用户把所有的数据都加载完成了,没有新的数据来触发 watch ,这就导致部分商品的数据准备好了但在页面上没有渲染,因此我们还需要针对这种情况再去做单独处理, ,我们可以额外加一个状态来判断 rightDataList + leftDataList 的总数是否等于 dataList,不等于的时候可以再触发一次 dataRender ......
class asyncQueue {
constructor() {
this.asyncList = [];
this.inProgress = false;
}
add(asyncFunc) {
return new Promise((resolve, reject) => {
this.asyncList.push({asyncFunc, resolve, reject});
if (!this.inProgress) {
this.execute();
}
});
}
execute() {
if (this.asyncList.length > 0) {
const currentAsyncTask = this.asyncList.shift();
currentAsyncTask.asyncFunc()
.then(result => {
currentAsyncTask.resolve(result);
this.execute();
})
.catch(error => {
currentAsyncTask.reject(error);
this.execute();
});
this.inProgress = true;
} else {
this.inProgress = false;
}
}
}
export default asyncQueue
每次调用 add 方法会往队列中添加经过特殊包装过的异步任务,并且只有在只有在没有正在执行中的任务的时候才开始执行 execute 方法。在每次执行异步任务时会从队列中 shift ,利用 promise.then 并且递归调用该方法,实现有序并且自动执行任务。在封装在这方法的过程中同样也使用到了我们的标记位大法 inProgress ,来保证我们正在执行当前队列时,突然又进来新的任务而导致队列执行错乱。const queue = new asyncQueue()
watch(() => props.dataList, async (newVal, oldVal) => {
queue.add(() => dataRender(newVal))
}, {
immediate: true,
deep: true
})
通过上述代码我们就可以,让我们的每一个异步任务有顺序的执行,并且让每一个异步任务执行完成以后自动执行下一个,完美的达到了我的需求。其实这个方法不仅适用于当前场景,我们很多的业务场景都会遇到这种情况,会被动接受多个请求,但是这些请求还要有序的执行,我们都可以使用这种方法。