尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

前端: 如何优化列表大批量的数据渲染

前端: 如何优化列表大批量的数据渲染
📅 发布时间:2026/6/18 19:28:11

需求点:如何列表数据渲染进行优化?

最近业务上也碰到这个问题点。上网也查了查资料,貌似也经常问,特此写文章记录下来。

关于如何处理以上上面的业务痛点:
就两点:
1 、虚拟列表是最主流的解决方案,不渲染所有的数据,只渲染可视区域中的数据。当用户滑(滚)动时,通过监听 scroll 来判断是上滑还是下拉,从而更新数据。同理 IntersectionObserver 和 getBoundingClientRect 都能实现
2、时间分片主要是分批渲染DOM,使用 requestAnimationFrame 来让动画更加流畅
tips: 第二点就不详细讲述了,文末有链接对应,可自行享用。

话不多说直接上代码:不懂的可下方评论或者私信:

<template><divref="list":style="{height}"class="infinite-list-container"@scroll="scroll($event)"><divref="phantom"class="infinite-list-phantom"/><divref="content"class="infinite-list"><div><divv-for="item in visibleData"ref="items":id="item._index":key="item._index"class="infinite-list-item"><!-- <slot ref="slot" :item="item.item"></slot> --><p><span style="color:red">{{ item.id }}</span>{{ item.value }}</p></div></div></div></div>
</template>
<script>
export default {props: {/** 需要渲染的数据 */listData: {type: Array,default: () => []},/** 每项高度 */itemSize: {type: Number,default: 200},/** 视口高度 */height: {type: String,default: '100%'}},data() {return {screenHeight: 0, // 可视区域高度startOffset: 0, // 偏移量start: 0, // 起始索引end: null // 结束索引};},computed: {/** 列表总高度 */listHeight() {return this.listData.length * this.itemSize;},/** 可显示的列表项数 */visibleCount() {return Math.ceil(this.screenHeight / this.itemSize);},/** 偏移量对应的style */getTransform() {return `transform3d(0, ${this.startOffset}px, 0)`;},/** 可显示列表数据 */visibleData() {return this.listData.slice(this.start, Math.min(this.end, this.listData.length));}},mounted() {this.screenHeight = this.$el.clientHeight;this.start = 0;this.end = this.start + this.visibleCount;},created() {this.initPositions();},updated() {this.$nextTick(function() {if (!this.$refs.items || !this.$refs.items.length) {return;}// 获取真实元素大小,修改对应的尺寸缓存this.updateItemsSize();// 更新列表总高度let height = this.positions[this.positions.length - 1].bottom;this.$refs.phantom.style.height = height + 'px';// 更新真实偏移量this.setStartOffset();});},methods: {/** 滚动事件 */scroll(e) {// 当前滚动位置let scrollTop = this.$refs.list.scrollTop;// 此时的开始索引this.start = this.getStartIndex(scrollTop);// 此时的结束索引this.end = this.start + this.visibleCount;// 此时的偏移量this.setStartOffset();},initPositions() {this.positions = this.listData.map((d, index) => ({index,height: this.estimatedItemSize,top: index * this.estimatedItemSize,bottom: (index + 1) * this.estimatedItemSize}));},/** 获取列表起始索引 */getStartIndex(scrollTop = 0) {return this.binarySearch(this.positions, scrollTop);},/** 二分查找 */binarySearch(list, value) {let start = 0;let end = list.length - 1;let tempIndex = null;while (start <= end) {let midIndex = parseInt((start + end) / 2);let midValue = list[midIndex].bottom;if (midValue === value) {return midIndex + 1;} else if (midValue < value) {start = midIndex + 1;} else if (midValue > value) {if (tempIndex === null || tempIndex > midIndex) {tempIndex = midIndex;}end = end - 1;}}return tempIndex;},/** 获取当前列表项的当前尺寸 */updateItemsSize() {let nodes = this.$refs.items;nodes.forEach(node => {let rect = node.getBoundingClientRect();let height = rect.height;let index = +node.id.slice(1);let oldHeight = this.positions[index].height;let dValue = oldHeight - height;// 存在差值if (dValue) {this.positions[index].bottom = this.positions[index].bottom - dValue;this.positions[index].height = height;for (let k = index + 1; k < this.positions.length; k++) {this.positions[k].top = this.positions[k - 1].bottom;this.positions[k].bottom = this.positions[k].bottom - dValue;}}});},/** 获取当前位置的便宜量 */setStartOffset() {let startOffset = this.start >= 1 ? this.positions[this.start - 1].bottom : 0;this.$refs.content.style.transform = `translate3d(0,${startOffset}px,0)`;}}
};
</script><style lang="less" scoped>
.infinite-list-container {overflow: auto;position: relative;-webkit-overflow-scrolling: touch;
}.infinite-list-phantom {position: absolute;left: 0;top: 0;right: 0;z-index: -1;
}.infinite-list {left: 0;right: 0;top: 0;position: absolute;
}.infinite-list-item {padding: 5px;color: #555;box-sizing: border-box;border-bottom: 1px solid #999;
}
</style>

这几篇文章讲述的很详细还有对应效果:
link
link2

相关新闻

  • tomcat启动一次问题的处理。
  • 软件开发 --- trae如何和环境配合执行
  • 应用安全 --- 如何反编译一个超大的函数

最新新闻

  • Citra图形设置终极指南:从模糊到高清的完整解决方案
  • 2026最新领英(LinkedIn)账户合规与风控申诉全指南:从算法机制到效率恢复实操
  • 完全掌握Blender资源宝典:从入门到实战的5大核心模块深度解析
  • C++多线程编程入门教程(非常详细)
  • 停止手动输入Prompt!AI编码圈的“循环工程”正在颠覆写代码的方式
  • TrafficMonitor插件:终极指南,让你的Windows任务栏变身全能信息中心

日新闻

  • 5分钟掌握Python进化算法:Geatpy高性能优化工具完全指南
  • Microchip 24AA044 EEPROM选型与应用全指南:从参数解析到实战编程
  • 华为的鸿蒙到底有多牛?为什么称作遥遥领先?

周新闻

  • 3步解锁iOS设备:applera1n激活锁绕过完全指南
  • 39 2026 人工智能证书终极盘点,普通人选 AI 证书可以从这些方向入手
  • Redis 暴露公网有多危险?从端口检查到补救步骤

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号