虚拟列表不能用 display: none 切换元素,因浏览器仍会为所有隐藏元素计算布局、生成渲染树,导致内存暴涨和卡顿;其核心是“不创建”而非“隐藏”,只渲染视口及缓冲区节点,dom 节点数应控制在 20–100 个以内。

虚拟列表为什么不能直接用 display: none 切换元素
因为浏览器仍会为所有 display: none 元素计算布局、生成渲染树,DOM 节点一多(比如 10 万条),内存暴涨、首次渲染卡死是常态。虚拟列表的核心不是“隐藏”,而是“不创建”——只渲染视口内及少量缓冲区的节点。
- 真实 DOM 节点数应控制在 20–100 个以内,和屏幕能显示的行数强相关
- 滚动时靠
scrollTop+ 容器高度动态算出当前应渲染的起始索引和数量 - 必须配合固定行高(或预设行高映射表)才能 O(1) 定位某项位置,否则无法快速滚动到指定项
React 中用 react-window 实现最小可行虚拟列表
react-window 是目前最轻量、无依赖、API 清晰的方案,比 react-virtualized 更适合新项目。它不封装数据逻辑,只负责定位与复用,你传什么数据它就渲染什么。
- 必须用
FixedSizeList(固定行高)或VariableSizeList(变高,需提供itemSize函数) - 不要把整个列表数据传给组件,只传
itemCount和itemData(可为数组引用或对象) - 关键 props:
height(容器像素高)、width(通常设为 '100%')、itemSize(数字或函数)、overscanCount(默认 5,建议设为 2–3 防白屏闪动)
<fixedsizelist height="{500}" width="100%" itemcount="{list.length}" itemsize="{48}" overscancount="{2}" itemdata="{list}">
{Row}
</fixedsizelist>
原生 JS 手写虚拟列表要注意的三个硬约束
不用框架也能做,但必须守住三条线,否则滚动撕裂、错位、卡顿立刻出现。
-
scroll事件不能直接监听——要用requestIdleCallback或节流 +passive: true,否则 iOS 上严重掉帧 - 必须用
position: absolute布局内容区,并通过top偏移模拟滚动,容器设overflow: hidden;不能靠transform: translateY()(部分安卓 WebView 渲染异常) - 每次滚动后要精确重算:
startIndex = Math.max(0, Math.floor(scrollTop / itemHeight)),再取slice(startIndex, startIndex + visibleCount + buffer),buffer 至少为 2 行
Vue 3 + vue-virtual-scroller 的坑:响应式失效和 key 冲突
这个库更新慢,Vue 3 的 setup + ref 响应式下容易漏更新。常见现象是数据变了但列表没重绘,或者滚动时某几行内容错乱。
- 别用
v-for直接遍历原始数组——必须用:items="list"+item-key="id",且id必须唯一、稳定(不能用 index) - 如果 list 是
ref([]),修改时必须用list.value = newList,不能 push/splice 原地改(否则内部 diff 失效) - 开启
fixed-item-height并显式传:item-height="48",否则默认按 56px 算,导致位置偏移
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











