10000+条数据的内容滚动功能如何实现?

3,968 阅读3分钟

遇到脑子有问题的产品经理该怎么办?如果有这么一个需求要你在一个可视区范围内不间断循环滚动几千上万条数据你会怎么去实现?

录制_2022_11_25_20_43_47_705.gif

且不说提这个需求的人是不是脑子有问题,这个需求能不能实现?肯定是可以的,把数据请求回来渲染到页面写个滚动样式就好了。抛开这样一次性请求上万条数据合不合理不讲,一万条数据渲染到页面上估计都要卡死了吧。那有没有更好的方法呢? 当然有

分析一波思路

image.png 我们分批次请求数据,比如可视化区域展示的是20条数据,那我们就一次只请求30条,然后把请求回来的数据保存起来,定义一个滚动的数组,把第一次请求的30条数据赋值给它。后面每当有一条数据滚出可视区域我们就把它删掉,然后往尾部新增一条,让滚动数组始终保持30条的数据,这样渲染在页面上的数据始终只有30条而不是一万条。文字描述太生硬我们上代码

首先定义两个数组,一个滚动区域的数组scrollList,一个总数据的数组totalList,模拟一个异步请求的方法和获取数据的方法。

<script lang="ts" setup>
import { nextTick, ref } from "vue";
type cellType = {
  id: number,
  title: string,
}
interface faceRequest {
  data: cellType,
  total: number
}
// 总数据的数组
const totalList = ref<Array<cellType>>([]);
 // 滚动的数组
const scrollList = ref<Array<cellType>>([]);
// 数据是否全部加载完毕
let loading: Boolean = false 
// 模拟异步请求
const request = () => {
  return new Promise<faceRequest>((resolve: any, reject: any) => {
    let data: Array<cellType> = []
    // 每次返回30条数据
    for (let i = 0; i < 30; i++) {
      data.push({
        id: totalList.value.length + i,
        title: 'cell---' + (totalList.value.length + i)
      });
    }
    let total = 10000// 数据的总数
    resolve({ data, total })
  })
}
const getData = () => {
  request().then(res => {
    totalList.value = totalList.value.concat(res.data)

    // 默认获取第一次请求回来的数据
    if (totalList.value.length <= 30) {
      scrollList.value = scrollList.value.concat(res.data)
    }
    // 当前请求的数量小于总数则继续请求
    if (totalList.value.length < res.total) {
      getData()
    } else {
      loading = true
    }
  })
}
getData()
</script>

上面写好了数据的获取处理,接下来写一下页面

<template>
  <div class="div">
    <div :style="styleObj" @mouseover="onMouseover" @mouseout="onMouseout" ref="divv">
      <div v-for="item in scrollList" :key="item.id" @click="onClick(item)">
        <div class="cell">{{ item.title }}</div>
      </div>
    </div>
  </div>
</template>
<script lang="ts" setup>
// 滚动样式
const styleObj = ref({
  transform: "translate(0px, 0px)",
});
</script>
<style scoped>
.div {
  width: 500px;
  height: 500px;
  background-color: aquamarine;
  overflow: hidden;
}

.cell {
  height: 30px;
}
</style>

现在页面跟数据的前提条件都写好,下面就是数据逻辑的处理了,也就是这篇文章的重点

  1. 获取页面上单条数据的总体高度
  2. 设置定时器使页面不停的滚动
  3. 当一条数据滚动出视图范围时调用处理数据的方法并且重置滚动高度为0
const divv = ref();
 // 当前滚动高度
const ScrollHeight = ref<number>(0);
// 储存定时器
const setInt = ref();
// 内容滚动
const roll = () => {
  nextTick(() => {
    let offsetHeight = divv.value.childNodes[1].offsetHeight
    setInt.value = setInterval(() => {
      if (ScrollHeight.value == offsetHeight) {
        onDel();
        ScrollHeight.value = 0;
      }
      ScrollHeight.value++;
      styleObj.value.transform = `translate(0px, -${ScrollHeight.value}px)`;
    }, 10);
  })
};
onMounted(() => {
  roll()
})

处理数据的方法

  1. 保存需要被删除的数据
  2. 删除超出视窗的数据
  3. 获取总数组的数据添加到滚动数组的最后一位
  4. 将被删除的数组数据添加到总数组最后面,
  5. 当滚动到最后一条数据时重置下标为0,使得数据首位相连不断循环
let index = 29;// 每次请求的数量-1,例如每次请求30条数据则为29
const onDel = () => {
  index++;
  if (loading) {
    // 当滚动到最后一条数据时重置下标为0
    if (index == totalList.value.length) {
      index = 0;
    }
    scrollList.value.shift();
    scrollList.value.push(totalList.value[index]);
  } else {
    if (index == totalList.value.length) {
      index = 0;
    }
    // 保存需要被删除的数据
    let value = scrollList.value[0]
    // 删除超出视窗的数据
    scrollList.value.shift();
    // 获取总数组的数据添加到滚动数组的最后一位
    scrollList.value.push(totalList.value[index]);
    // 将被删除的数组数据添加到总数组最后面
    totalList.value.push(value)
  }
};

到这里代码就写好了,接下来让我们看看效果怎么样

image.png

总结

在我们开发的过程中会遇到各种各样天马行空的需求,尤其会遇到很多不合理的需求,这时候我们就要三思而后行,

想清楚能不能不做?

能不能下次再做?

能不能让同事去做?

image.png 注意:功能实现效果的方式有很多种,该篇文章中使用的一些方式不一定是最好的,只是我刚好想到,如果大家有更有好的实现方式欢迎在评论区讨论哈