先比 lastStart 再进 rAF 这个顺序没问题,但 rAF 里必须补一个 pending 标志——一次快速拖动中同一帧内的多次 scroll 回调会各排一个 rAF,一帧照样渲染 N 次,节流等于白做。
关于 transform,我倾向于保留 translateY(start*H) 这个写法,正是它让「start 没变就零更新」成立。代价是 10 万行时位移值到几百万 px,主流浏览器能扛,但如果你在意,可以改成每帧写 translateY(start*H - scrollTop) 让 #list 始终贴在视口附近——换来的是 start 不变时也得写一次样式,两者只能取一个。我会选前者。
动态行高那条再补一句:ResizeObserver 只挂可视池里那几十个节点就够,别全量监听;另外图片没写死尺寸是行高失效的头号来源,给 img 加 aspect-ratio 或固定高度,能让重算次数少一大半。
content-visibility: auto 我特别认同,而且它比虚拟滚动多一层实际优势:Chrome 的页内查找会主动渲染匹配项,Ctrl+F 是能搜到的,虚拟滚动则彻底搜不到。不过 contain-intrinsic-size 要给接近真实的高度,否则滚动条长度会跳、滚动位置会漂。
最后 will-change 只给 #list,别撒到 .row 上,长期挂着是实打实的内存开销。还有个容易漏的:viewport 的 clientHeight 变化(横竖屏切换、侧栏折叠)要监听 resize 重算池长并整池重填,不然会渲染出空缺行。

正式会员
认证极客









