十万条数据怎么办?Vue3虚拟列表让你纵享丝滑 1. 技术背景虚拟滚动Virtual Scrolling是一种优化长列表渲染性能的技术。当列表数据量很大时传统方式渲染所有 DOM 元素会导致严重的性能问题。虚拟滚动通过只渲染可视区域内的元素大幅减少 DOM 节点数量从而提升渲染性能和用户体验。2. 固定高度(v1)2.1 核心实现原理该组件基于以下核心思想实现通过一个固定高度的容器作为视口viewport使用一个与全部数据等高的占位元素scroll-bg产生滚动条动态计算并渲染可见区域内的数据项利用 translate3d 实现高效的列表项定位2.2 关键代码解析模板结构template div classviewport refviewport scrollhandleScroll div classscroll-bg refscrollBg/div div classscroll-list refscrollList :style{ transform: translate3d(0, ${offset}px,0) } template v-foritem in visibleItems :keyitem.id slot :itemitem/slot /template /div /div /templateProps 定义const props defineProps{ items: any[]; // 列表数据 remain: number; // 显示个数 size: number; // 每个元素的高度 }();核心状态管理用start和end来记录当前显示的屏幕需要显示的数组的起始位置但是在滚动过程中会存在一个列表元素滚动了一部分的情况会有空白的显示问题所以在增加前后预加载的数据相当于加载3个屏幕的数据用offset记录显示列表数据的父元素的偏移量偏移量为用户滑动过完整的列表个数和列表高度的乘积对传入的总数据进行截取只显示预加载和屏幕正在显示的列表数据以此来提高渲染性能提高用户体验// 数组的起始值 const start ref(0); const end ref(props.remain); // 数组渲染dom的偏移量 const offset ref(0); // 前面预先加载的个数 const prevCount computed(() { return Math.min(start.value, props.remain); }); // 后面预先加载的个数 const nextCount computed(() { return Math.min(props.items.length - end.value, props.remain); }); // 计算当前需要显示的数据 const visibleItems computed(() { const startIndex start.value - prevCount.value; const endIndex end.value nextCount.value; return props.items.slice(startIndex, endIndex); });滚动事件处理获取滚动的距离重新计算屏幕中显示数据的开始位置和结束位置更新偏移量const handleScroll () { // 滚动的距离 const scrollTop viewportRef.value?.scrollTop ?? 0; // 滚动过去的完整个数 const scrollCount Math.floor(scrollTop / props.size); start.value scrollCount; end.value start.value props.remain; offset.value start.value * props.size - prevCount.value * props.size; };2.3 性能优化策略只渲染可视区域内的元素减少 DOM 节点数量使用 translate3d 进行硬件加速提高动画性能预加载前后缓冲区数据避免快速滚动时白屏利用 Vue 的响应式系统精确更新数据切片2.4 使用示例template virtual-scroll-list :size40 :itemsitems :remain10 template v-slot{ item } constant-item :titleitem.title/constant-item /template /virtual-scroll-list /template3. 动态高度(v2)当列表的内容是不确定的可变的时候固定高度不再满足业务需求需要增加可变的选项来满足可变高度的列表3.1 核心实现原理3.1.1 Position Cache位置缓存维护每个列表项的位置信息top, bottom, height初始时基于默认大小预估位置动态更新实际渲染后的准确位置3.1.2 Binary Search二分查找在可变高度模式下使用二分查找快速定位可见区域的起始索引相比线性查找大幅提升滚动性能3.2 算法详解3.2.1 二分查找算法用于快速定位当前滚动位置对应的起始项索引区别于平常的二分算法由于可能滚动的位置在一个列表元素的中间位置所以增加temp变量来记录当前当前最上方显示的元素在数据列表中的索引const binarySearch (scrollTop) { let start 0; let end positions.length - 1; let temp null; while (start end) { let mid (start end) | 0; let midBottom positions[mid].bottom; if (scrollTop midBottom) { return mid 1; } else if (scrollTop midBottom) { if (temp null || temp mid) { temp mid; } end mid - 1; } else { start mid 1; } } return temp || 0; };3.2.2 位置更新算法当元素高度发生变化时更新位置缓存const { height } el.getBoundingClientRect(); const id Number(el.getAttribute(vid)) || 0; const oldHeight positions.find((p) p.id id)?.height ?? 0; const diffHeight height - oldHeight; if (diffHeight ! 0) { // 高度有变化 const index positions.findIndex((p) p.id id); positions[index]!.height oldHeight diffHeight; positions[index]!.bottom positions[index]!.top height; //后面的都需要更新 for (let i index 1; i positions.length; i) { positions[i]!.top positions[i - 1]!.bottom; positions[i]!.bottom positions[i]!.top positions[i]!.height; } }3.3 性能优化3.3.1 节流处理滚动事件使用lodash.throttle进行节流处理默认100ms间隔。3.3.2 预加载机制根据前后预加载数量(prevCount/nextCount)渲染额外项减少快速滚动时的白屏现象。该组件可以轻松处理包含上万条数据的列表同时保持流畅的滚动体验是处理大数据列表渲染的理想解决方案。团队介绍「智慧家技术平台-智家APP开发」通过持续迭代演进移动端一站式接入平台为三翼鸟APP、智家APP等多个APP提供基础运行框架、系统通用能力API、日志、网络访问、页面路由、动态化框架、UI组件库等移动端开发通用基础设施通过Z·ONE平台为三翼鸟子领域提供项目管理和技术实践支撑能力完成从代码托管、CI/CD系统、业务发布、线上实时监控等Devops与工程效能基础设施搭建。