后端一次性返回大量数据,前端如何处理优化?
1、使用定时器分组分批分堆依次渲染(定时加载、分堆思想)
自定义一个函数,将数据进行分堆
所谓分堆思想其实就是
一次截取一定长度的数据
-
比如原来的数据是:
[ 1, 2, 3, 4, 5, 6, 7, 8 ] // 一次截取一定长度的数据,这里模拟一次截取3条 // 截取后的数据格式 [ [1,2,3], [4,5,6], [7,8] ]
可以看到经过分堆之后的数据结构变成了一个二维数组,我们接下来就只需要遍历这个二维数组,运用定时器分堆渲染数据即可
let data = [];
// 分组函数
// @return [][]
function average(arr){
let temp = 0;// 从第0个位置开始分组
const result = []; // 接收最终分组结果的数组
const pageSize = 10; // 每组分组数量
while(temp<arr.length){
result.push(arr.slice(temp, temp + pageSize))
temp= temp + pageSize
}
return result;
}
// 定时器渲染
// @return null
async function render(){
const res = await axios.get("http://xxxxxx");
// ... 假设这里 res 就是我们需要的大批量数据的数组
const result = average(res);
for(let i = 0;i < result.length;i++){
setTimeout(() => {
data = [...data, ...result[i]]; // 渲染
}, 1000 * i);
}
}
2、使用requestAnimationFrame代替定时器去做渲染
使用请求requestAnimationFrame与定时器渲染方法变化不大,只需要改变render函数中的写法即可
// requestAnimationFrame 代替定时器渲染
// @return null
async function render(){
const res = await axios.get("http://xxxxxx");
// ... 假设这里 res 就是我们需要的大批量数据的数组
const result = average(res);
const cb = (page) => {
if(page > result.length - 1){
return
}
requestAnimationFrame(() => {
data = [...data, ...result[page]]; // 渲染
page = page+1;
cb(page);
});
}
cb(0);
}
3、使用getBoundingClientRect延迟加载
当数据量过大时,用户屏幕显示的数据量有限,这时我们可以根据用户的滚动位置,采用延迟加载策略动态渲染数据。
要获取用户的滚动位置,我们可以在列表末尾添加一个空节点空白。每当视口出现空白时,就意味着用户已经滚动到网页底部,这意味着我们需要继续渲染数据。
使用elem.getBoundingClientRect()来判断空白是否在页面底部。
<script setup lang="ts">
import { onMounted, ref, computed } from 'vue'
// @return []
const getList = () => {
// ... 异步请求获取数据并返回
}
const wrapper = ref<HTMLElement>();
const blank = ref<HTMLElement>();
const list = ref<any>([]);
const page = ref(1);
const limit = 200;
const maxPage = computed(() => Math.ceil(list.value.length / limit))
const renderList = computed(() => list.value.slice(0, page.value * limit))
const handleScroll = () => {
if (page.value > maxPage.value) return
const clientHeight = container.value?.clientHeight
const blankTop = blank.value?.getBoundingClientRect().top
if (clientHeight === blankTop) {
page.value++
}
}
onMounted(async () => {
list.value = await getList()
})
</script>
<template>
<div @scroll="handleScroll" ref="wrapper">
<div v-for="(item) in renderList" :key="item.id">
<span>{{ item.text }}</span>
</div>
<div ref="blank"></div>
</div>
</template>