后端一次性返回大量数据,前端如何处理优化?

1,248 阅读2分钟

后端一次性返回大量数据,前端如何处理优化?

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()来判断空白是否在页面底部。

1683269934947.png

<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>