别再抱怨后端一次性传给你 1w 条数据了,几行代码教会你虚拟滚动!

如果后端一次性传给你 1 万条数据,该怎么办,当然是让他圆润的走开,哈哈,开个玩笑。虽然这种情况很少,不过我在实际开发中还真遇到了类似的情况,接下来我将基于 vue3 实现一个简单的虚拟滚动。

我们都知道,如果一次性展示所有的数据,那么会造成页面卡顿,虚拟滚动的原理就是将数据根据滚动条的位置进行动态截取,只渲染可视区域的数据,这样浏览器的性能就会大大提升,废话不多说,我们开始。

具体实现

首先,我们先模拟 500 条数据

js 复制代码
const data = new Array(500).fill(0).map((_, i) => i); // 模拟真实数据

然后准备以下几个容器:

html 复制代码
<template>
  <div class="view-container">
    <div class="content-container"></div>
    <div class="item-container">
      <div class="item"></div>
    </div>
  </div>
</template>
  • view-container是展示数据的可视区域,即可滚动的区域
  • content-container是用来撑起滚动条的区域,它的高度是实际的数据长度乘以每条数据的高度,它的作用只是用来撑起滚动条
  • item-container是实际渲染数据的区域
  • item则是具体渲染的数据

我们给这几个容器一点样式:

css 复制代码
.view-container {
  height: 400px;
  width: 200px;
  border: 1px solid red;
  overflow-y: scroll;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.content-container {
  height: 1000px;
}

.item-container {
  position: absolute;
  top: 0;
  left: 0;
}

.item {
  height: 20px;
}

view-container固定定位并居中,overflow-y设置为scroll

content-container先给它一个1000px的高度;

item-container绝对定位,topleft都设为 0;

每条数据item给他一个20px的高度;

先把 500 条数据都渲染上去看看效果:

这里我们把高度都写死了,元素的高度是实现虚拟滚动需要用到的变量,因此肯定不能写死,我们可以用动态绑定style来给元素加上高度:

首先定义可视高度和每一条数据的高度:

js 复制代码
const viewHeight = ref(400); // 可视容器高度
const itemHeight = ref(20); // 每一项的高度

用动态绑定样式的方式给元素加上高度:

html 复制代码
<div class="view-container" :style="{ height: viewHeight + 'px' }">
  <div
    class="content-container"
    :style="{
        height: itemHeight * data.length + 'px',
      }"
  ></div>
  <div class="item-container">
    <div
      class="item"
      :style="{
          height: itemHeight + 'px',
        }"
    ></div>
  </div>
</div>

content-container 使用每条数据的高度乘以数据总长度来得到实际高度。

然后我们定义一个数组来动态存放需要展示的数据,初始展示前 20 条:

js 复制代码
const showData = ref<number[]>([]); // 显示的数据
showData.value = data.slice(0, 20); // 初始展示的数据 (前20个)

showData里的数据才是我们要在item遍历渲染的数据:

html 复制代码
<div
  class="item"
  :style="{
          height: itemHeight + 'px',
        }"
  v-for="(item, index) in showData"
  :key="index"
>
  {{ item }}
</div>

接下来我们就可以给view-container添加滚动事件来动态改变要展示的数据,具体思路就是:

  1. 根据滚动的高度除以每一条数据的高度得到起始索引
  2. 起始索引加上容器可以展示的条数得到结束索引
  3. 根据起始结束索引截取数据

具体代码如下:

js 复制代码
const scrollTop = ref(0); // 初始滚动距离
// 滚动事件
const handleScroll = (e: Event) => {
  // 获取滚动距离
  scrollTop.value = (e.target as HTMLElement).scrollTop;
  // 初始索引 = 滚动距离 / 每一项的高度
  const startIndex = Math.round(scrollTop.value / itemHeight.value);
  // 结束索引 = 初始索引 + 容器高度 / 每一项的高度
  const endIndex = startIndex + viewHeight.value / itemHeight.value;
  // 根据初始索引和结束索引,截取数据
  showData.value = data.slice(startIndex, endIndex);

  console.log(showData.value);
};

打印一下数据看看数据有没有改变:

可以看到数据是动态改变了,但是页面上却没有按照截取的数据来展示,这是因为什么呢? 查看一下元素:

可以看到存放数据的元素 也就是 item-container 也跟着向上滚动了,所以我们不要让它滚动,可以通过调整它的 translateY 的值来实现,使其永远向下偏移滚动条的高度

html 复制代码
<div
  class="item-container"
  :style="{
          transform: 'translateY(' + scrollTop + 'px)',
        }"
>
  <div
    class="item"
    :style="{
            height: itemHeight + 'px',
          }"
    v-for="(item, index) in showData"
    :key="index"
  >
    {{ item }}
  </div>
</div>

看效果:

文章到此就结束了。这只是一个简单的实现,还有很多可以优化的地方,例如滚动太快出现白屏的现象等,大家可以尝试一下,并试着优化一下。

希望本文能够对你有帮助。

相关推荐
青皮桔9 分钟前
CSS实现百分比水柱图
前端·css
失落的多巴胺9 分钟前
使用deepseek制作“喝什么奶茶”随机抽签小网页
javascript·css·css3·html5
DataGear13 分钟前
如何在DataGear 5.4.1 中快速制作SQL服务端分页的数据表格看板
javascript·数据库·sql·信息可视化·数据分析·echarts·数据可视化
影子信息14 分钟前
vue 前端动态导入文件 import.meta.glob
前端·javascript·vue.js
青阳流月16 分钟前
1.vue权衡的艺术
前端·vue.js·开源
RunsenLIu17 分钟前
基于Vue.js + Node.js + MySQL实现的图书销售管理系统
vue.js·mysql·node.js
样子201820 分钟前
Vue3 之dialog弹框简单制作
前端·javascript·vue.js·前端框架·ecmascript
kevin_水滴石穿21 分钟前
Vue 中报错 TypeError: crypto$2.getRandomValues is not a function
前端·javascript·vue.js
翻滚吧键盘21 分钟前
vue文本插值
javascript·vue.js·ecmascript
华子w90892585922 分钟前
基于 SpringBoot+Vue.js+ElementUI 的 “花开富贵“ 花园管理系统设计与实现7000字论文
vue.js·spring boot·elementui