前端性能优化:用虚拟列表轻松渲染 100000 条数据

引言

你是否遇到过这样的场景:要渲染上万条数据到页面,一次性全部插入 DOM,页面直接卡到怀疑人生?

其实,大多数时候用户只会看到可视区域的几十条数据。因此我们可以用虚拟列表:只渲染可见区域,随着滚动动态更新节点,让 100000 条数据也能轻松运行。

核心思路:只渲染可见区域

  1. 用一个「撑高元素」模拟总高度(这样滚动条长度是正确的)。
  2. 根据滚动条位置计算当前可见区域的数据索引。
  3. 只渲染正确的数据,并通过 translateY 把可视区域定位到正确位置。

核心概念

1. 容器高度:控制可视区域的大小(决定一次能看到几行)
2. 每行高度:固定行高,便于计算定位
3. 开始索引 (startIndex):滚动到哪里,就从哪一行开始渲染
4. 结束索引 (endIndex):可视区域最后一行的索引
5. 可见行数量 (visibleCount):(容器高度 / 行高)+ 1,比如可视化区域为300,行高为200,有可能第一行和第三行会同时出现,所以可见行需要 +1 作为缓冲
6. 容器状态变化:用户滚动时,scrollTop 变化 → 重新计算索引 → 更新可见数据 → DOM 复用
初始化状态

startIndex = 0,endIndex = 10,可视区域y轴偏移量0

页面滚动之后的状态

startIndex = 5,endIndex = 15,可视区域y轴偏移量150px 我们始终看到的是可视区域的变化,初始化时候,可视化区域内可能是item1,item2,item3,页面滚动之后,可视化区域内变为item5,item6,item7,渲染的列表始终只有可视化区域内容纳的列表数量。

最终实现之后,可视化区域的dom节点在来回替换,10万个数据渲染只需不到1s即可。

Demo

js 复制代码
<template>
  <div
    ref="container"
    class="list-container"
    @scroll="onScroll"
  >
    <!-- 占位撑起整体高度 -->
    <div class="list-phantom" :style="{ height: listHeight + 'px' }"></div>

    <!-- 可视区渲染的节点 -->
    <div
      class="list"
      :style="{ transform: `translateY(${startOffset}px)` }"
    >
      <div
        v-for="(item, index) in visibleData"
        :key="item"
        class="list-item"
      >
        {{ item }}
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, onMounted } from "vue";

const itemHeight = 50;           // 每一项的高度
const containerHeight = 300;     // 容器高度
const listData = Array.from({ length: 100000 }, (_, i) => `Item ${i + 1}`);

const container = ref(null);
const scrollTop = ref(0);

const visibleCount = Math.ceil(containerHeight / itemHeight) + 1; // 额外多渲染1行避免空白

const startIndex = computed(() => Math.floor(scrollTop.value / itemHeight));
const endIndex = computed(() => startIndex.value + visibleCount);
const visibleData = computed(() => listData.slice(startIndex.value, endIndex.value));

const startOffset = computed(() => startIndex.value * itemHeight);
const listHeight = listData.length * itemHeight;

function onScroll() {
  scrollTop.value = container.value.scrollTop;
}
onMounted(() => {
  container.value.style.height = containerHeight + "px";
});
</script>

<style scoped>
.list-container {
  overflow-y: auto;
  border: 1px solid #ccc;
  position: relative;
}

.list-phantom {
  width: 100%;
}

.list {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
}

.list-item {
  height: 50px;
  line-height: 50px;
  border-bottom: 1px solid #eee;
  padding-left: 12px;
  box-sizing: border-box;
}
</style>

也有些第三方库是很不错的,比如:vue-virtual-scroller;也没必要重复造轮子了,当练练手还是不错的

相关推荐
@PHARAOH14 分钟前
WHAT - cursor cli 开发范式
前端·ai·ai编程
子兮曰1 小时前
深入 HTML-in-Canvas:当 Canvas 学会了渲染 DOM,前端图形生态要变天了
前端·javascript·canvas
ws_qy1 小时前
从大模型原理到前端 AI Coding 工程化实践
前端·ai编程
倾颜2 小时前
React 19 源码主线拆解 04:Fiber 到底是什么,React 为什么需要 Fiber?
前端·react.js·源码阅读
AI攻城狮2 小时前
国产大模型能力大比拼,社区有话说
前端
IT_陈寒2 小时前
Vite的public文件夹放静态资源?这坑我替你踩了
前端·人工智能·后端
涵涵(互关)3 小时前
GoView各项目文件中的相关语法2
前端·javascript·vue.js
子兮曰3 小时前
别让爬虫白嫖你的导航站了:纯免费,手把手实现加密字体防爬
前端·javascript·后端
小村儿3 小时前
连载06 - Hooks 源码深度解析:Claude Code 的确定性自动化体系
前端·后端·ai编程
心中无石马3 小时前
uniapp引入tailwindcss4.x
前端·css·uni-app