虚拟列表通过仅渲染视口及缓冲区节点、用 height 占位、滚动时仅更新 transform 和局部 dom 来解决长列表卡顿;需固定行高或缓存高度、精准位置计算与动态挂载/卸载。

虚拟列表为什么能解决滚动卡顿
长列表(比如上万条数据)直接渲染会触发大量 DOM 创建和重排,浏览器主线程被持续占用,滚动必然卡顿。virtual-list 的核心不是“懒加载”,而是只渲染视口内及少量缓冲区的节点,其余节点用 height 占位。关键在于:滚动时只更新 transform: translateY() 和局部 DOM,不触发布局重排。
常见错误是把“节流滚动事件 + 显示部分数据”当成虚拟列表——这只能缓解,不能根治。真虚拟列表必须配合固定行高(或预估高度缓存)+ 位置计算 + 动态挂载/卸载。
React 中用 react-window 实现最小可行虚拟列表
react-window 是目前最轻量、兼容性好、API 清晰的方案。它不依赖 ref 或自定义 hook 就能跑通,适合快速落地。
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
- 必须提供
itemCount(总条数),不能传动态变化的数组长度再实时 filter —— 否则索引错乱 - 每行高度要固定,用
FixedSizeList;若高度不一,改用VariableSizeList并传itemSize函数,但需自己缓存每项高度,否则反复计算拖慢滚动 -
itemData属性用于透传原始数据,避免在itemRenderer里闭包捕获大对象导致内存泄漏
import { FixedSizeList as List } from 'react-window';
// ...
<list height="{600}" itemcount="{items.length}" itemsize="{48}" width="100%" itemdata="{items}">
{Row}
</list>
Vue 3 + vue-virtual-scroller 的坑与绕过方式
vue-virtual-scroller 默认使用 RecycleScroller,但它的“回收机制”在某些场景下会误删正在过渡动画的节点,导致闪烁。更严重的是,它对 slot-scope 的响应式追踪不够稳定,props 更新后子组件可能不重绘。
- 禁用自动回收:
:disable-recycling="true",改用DynamicScroller+ 手动控制min-item-size和buffer-size - 避免在
v-for内部用computed或深层响应式对象,改用toRefs(props.item)解构基础字段 - 如果用了
IntersectionObserver做图片懒加载,必须给每个<img>加key,否则复用节点时 observer 不会重新绑定
自研简易虚拟列表的关键判断点
当框架方案无法满足定制需求(如嵌套滚动、复杂 sticky 头、服务端分页混合渲染),就得手写。但别从零算 offset —— 直接复用 getBoundingClientRect() + scrollTop 是最稳的起点。
- 首屏渲染必须同步完成,不能等
scroll事件触发才计算可见范围,否则白屏明显 - 缓冲区大小建议设为视口高度的 1.5 倍,太小易露白,太大抵消性能收益
- 滚动结束后的“定位修正”逻辑(比如跳转到某 ID 行)必须用
requestIdleCallback延迟执行,否则阻塞下一帧 - 移动端要注意
touchmove的 passive 默认值,{ passive: false }必须显式声明,否则 iOS Safari 会忽略preventDefault
最常被忽略的其实是键盘导航支持:虚拟列表节点不可 focus,tabindex 和 focus() 调用得手动补全,否则无障碍访问直接失效。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










