学完这篇你能搞清楚 contain 的几个取值分别隔离什么、哪类场景该用哪一档,以及怎么在 DevTools 里验证改动真的减少了重排重绘。
第一步:先定位重排重绘发生在哪
打开 Chrome DevTools → Performance 面板 → 点录制 → 在页面上滚动或触发交互 → 停止。火焰图里紫色(Layout)和绿色(Paint)条就是我们要压缩的对象。如果卡片内一个小动画就能让整页 Layout 拉得很长,说明布局计算范围被放大,这正是 contain 要解决的问题:告诉浏览器「这块内部的变化,不用外溢出去重算」。
第二步:认识取值,别一上来就全开
| 值 | 隔离什么 | 副作用 |
|---|
layout | 内外布局互不影响;元素成为绝对/固定定位后代的包含块,并创建层叠上下文 | position: fixed 后代改为相对本元素定位 |
paint | 后代不绘制到元素边界之外(强制裁剪) | 溢出内容会被切掉 |
size | 元素尺寸不再依赖内容 | 未显式给尺寸时会塌成 0 |
style | 计数器、引号作用域 | 收益极小,可忽略 |
content | = layout paint style | 同上,最常用的一档 |
strict | = size layout paint style | 需要宽高都固定 |
第三步:给卡片加 contain: layout paint
.card { contain: layout paint; }
性价比最高的一档:不带 size,卡片仍由内容撑开不会塌陷,同时把内部布局与绘制限死在卡片内。适用于信息流卡片、评论区单条、聊天消息块这类「高度自适应但内容频繁变化」的元素。
注意:卡片里如果有超出边界的气泡提示、下拉菜单、tooltip,paint 会直接裁掉它们。这类元素要么改结构放到卡片外,要么只写 contain: layout。
第四步:长列表用 content-visibility: auto
.feed-item {
content-visibility: auto;
contain-intrinsic-size: auto 200px;
}
content-visibility: auto 在元素离开视口时跳过渲染,相当于隐式带上 layout style paint 与尺寸隔离。contain-intrinsic-size 给它一个占位高度,auto 200px 表示「先用 200px,渲染过一次后记住真实值」,避免滚动条乱跳。屏幕外的几十上百条不再参与布局与绘制,首屏外的成本几乎为零。
注意:占位高度估得太离谱会导致滚动条抖动、锚点跳转错位,先用 DevTools 量一下真实高度;依赖页内 Ctrl+F 查找全文的场景各浏览器行为有差异,正文搜索类页面慎用。
第五步:尺寸写死的容器上 strict
图表容器、canvas 容器、固定比例的广告卡槽:
.chart-box { width: 100%; height: 320px; contain: strict; }
宽高都定死了,浏览器彻底不用看内容就算尺寸,收益最彻底。
注意:不要给 html、body 或「需要让 fixed 弹窗相对视口定位」的祖先元素加 contain: layout/paint;加入后 fixed 后代改为相对该元素定位,弹窗、吸顶导航会直接错位。排查这类问题先翻祖先链。
第六步:回到 Performance 验证
再录一次同样的操作,对比 Layout 次数与总时长;Rendering 面板勾 Paint flashing 看绿色区域是否缩小,勾 Layout Shift Regions 看有没有引入新位移。数据没变好说明这层隔离没打中瓶颈,别硬留。
小结
contain 的本质是向浏览器承诺「这块自成一体」,换取布局与绘制范围的收缩
- 档位选择:
layout paint(通用安全)→ content(列表卡片)→ strict(宽高写死的容器)
- 三个必知副作用:
paint 裁剪溢出、layout 改变 fixed 参照、size 让元素塌成 0
- 顺序别搞反:先用 Performance 找到真实 Layout 热点,再决定加在哪一层,不要全站铺开