contain: paint 能减少长列表重绘,因它切断容器内外绘制关联,使浏览器无需为整个列表重绘;实测1000+条目滚动帧率从20fps升至58–60fps;但需避免用于含绝对定位重叠或依赖 overflow: hidden 裁剪的场景,且 iOS Safari 不支持 contain: strict。

contain属性为什么能减少长列表重绘
浏览器对未被 contain 限制的元素,会默认保留其子树与页面其余部分的布局、样式、绘制关联。滚动长列表时,哪怕只动一屏,浏览器仍可能为整个列表容器做样式计算甚至重绘——尤其当列表项里有 transform、opacity 或伪类动画时。
加 contain: paint 后,浏览器明确知道:“这个容器内部的绘制不会影响外部,外部变化也不需要重绘它”,于是大幅收缩重绘区域。实测中,1000+ 条目列表滚动帧率从卡顿的 20fps 提升到稳定 58–60fps(iOS Safari 16.4+ / Chrome 115+)。
注意:不是所有场景都适合。如果列表项之间有绝对定位重叠、或依赖父容器 overflow: hidden 裁剪,contain: paint 可能导致裁剪失效或定位偏移。
移动端必须用 contain: paint 而不是 strict
contain: strict 等价于 layout style paint size,看似“更彻底”,但在 iOS Safari 上存在已知兼容问题:开启后,某些含 position: sticky 的表头或滚动锚点会失灵;Android Chrome 旧版本(
立即学习“前端免费学习笔记(深入)”;
真实项目中更稳妥的做法是分层控制:
-
contain: paint加在滚动容器(如.list-wrapper)上,解决主要重绘压力 - 若需进一步隔离布局影响(比如列表高度由 JS 动态计算),再单独对每个
.list-item加contain: layout paint - 永远避免在根滚动容器(
body或#app)上设contain: strict,会导致页面级交互响应延迟
contain 和 will-change 冲突时优先选谁
will-change: transform 曾被滥用作“性能急救药”,但它会强制提升图层、增加内存开销,且无法被浏览器自动降级。而 contain: paint 是声明式约束,由浏览器按需优化,无副作用。
两者同时存在时,Chrome 会优先尊重 contain 的边界语义,will-change 可能被忽略;Safari 则可能因冲突直接放弃优化。
实操建议:
- 删掉所有
will-change,改用contain: paint+transform: translateZ(0)(仅当真有 3D 动画需求) - 如果列表项内有频繁动画(如 loading 骨架屏),对动画元素单独加
contain: paint,而不是整行加 - 用
chrome://tracing对比开启前后的 “Paint” 和 “Layout” 时间块,确认是否真正收敛
兼容性兜底和检测写法
Android WebView 低版本(Chrome/74)、微信内置 X5 内核(X5/6.12)不支持 contain,直接写会静默失效。不能靠 @supports 兜底,因为部分安卓浏览器虽识别语法但行为异常。
推荐检测方式:
if ('contain' in document.documentElement.style) {
document.documentElement.classList.add('has-contain');
}
然后 CSS 中这样写:
.list-wrapper {
contain: paint;
}
.has-contain .list-wrapper {
contain: paint;
}
关键点:不要用 JavaScript 动态加 style.contain,部分低端机 JS 执行时机晚于首帧渲染,起不到优化效果。
真正难处理的是 hybrid 容器里的 webview —— 得配合客户端发版才能升级内核,这时候只能退回到虚拟滚动方案,contain 不是银弹。


















