别再抱怨后端一次性传给你 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>

看效果:

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

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

相关推荐
用户3502015884748几秒前
基于react-routet v7 的配置式 + 约定式路由系统 第二步:一个简单的约定式路由系统
前端
攀登的牵牛花4 分钟前
前端向架构突围系列 - 框架设计(七):反应式编程框架Flower的设计
前端·架构
佛系打工仔7 分钟前
K线绘制前言
前端
遇见~未来32 分钟前
JavaScript数组全解析:从本质到高级技巧
开发语言·前端·javascript
哈__32 分钟前
基础入门 React Native 鸿蒙跨平台开发:TabBar 底部导航栏
javascript·react native·react.js
lili-felicity34 分钟前
React Native 鸿蒙跨平台开发:Animated 实现鸿蒙端组件的左右滑动动画
javascript·react native·react.js
石像鬼₧魂石36 分钟前
80 端口(Web 服务)渗透测试完整总结(含踩坑 + 绕过 + 实战流程)
linux·运维·服务器·前端·网络·阿里云
哈__44 分钟前
React Native 鸿蒙跨平台开发:StatusBar 状态栏组件
javascript·react native·react.js
C_心欲无痕1 小时前
nginx - 核心概念
运维·前端·nginx
开开心心_Every1 小时前
安卓做菜APP:家常菜谱详细步骤无广简洁
服务器·前端·python·学习·edge·django·powerpoint