直接给容器加上 `contain: layout paint` 就能起效——但前提是容器得有明确尺寸(比如写了 `height` 或 `min-height`),否则浏览器会静默忽略。这不是 bug,是规范强制要求。

### 为什么 `contain: layout paint` 是实用起点
单用 `contain: layout` 管不住子元素颜色变化引发的样式重算;单用 `contain: paint` 挡不住子元素撑大容器导致的外部重排。两者必须一起上,才能同时切断布局影响链和绘制扩散链。
下面这几个关键判断值得记住:
- `contain: content` 在 Chrome 120+ 已被废弃,MDN 明确标记为不推荐,行为不稳定,别再往上用了
- `contain: strict` 等价于 `layout paint style size`,但加了 `size` 之后,容器会彻底忽略子内容的高度贡献——列表项高度浮动时,一不小心就可能塌成 0px
- 移动端长列表、折叠面板、徽标组件这些场景,`contain: layout paint` 能覆盖 90% 的有效用例,而且兼容性好(Chrome 56+、Firefox 69+、Safari 15.4+)
### 容器没写宽高,`contain` 就等于没写
浏览器要求 `contain: layout` 或 `contain: strict` 的容器必须有可预测的尺寸上下文,否则直接跳过去。查 `getComputedStyle(el).contain` 虽然能看到声明,但 DevTools 里的 Paint Flashing 该闪还是闪,Layout Shift Regions 该红还是红。
几个实用原则:
- 显式写 `width` 和 `height` 最稳妥,比如卡片固定高:`height: 120px`
- 响应式场景优先用 `min-height` 或 `aspect-ratio` 配合 `width: 100%`,避开 `min-content`、`fit-content` 这类依赖内容的值
- Flex/Grid 容器内的子项加 `contain`,如果父容器没设确定尺寸,同样会失效
### JS 动态设置 `contain` 容易踩的三个坑
不是调 `el.style.contain = 'layout paint'` 就完事,时机、配套操作和兜底判断缺一不可。
- 插入大量子节点前先设 `contain`,否则插入过程会触发父级重排;插入后如果需要动画展开,记得收起时清空 `el.style.contain = ''`,不然 `size` 可能把高度锁死
- 监听 `resize` 时,只对已知固定尺寸的区域(比如顶部工具栏)补 `layout paint`,动态宽度的侧边栏不能加 `size`
- 生产环境务必检测支持:`if ('contain' in document.documentElement.style)`,Safari 对 `size` 的实现仍有偏差,iOS 微信 X5 内核基本不支持 `strict`
### 验证是否真生效,别信 `getComputedStyle`
声明写了、控制台查得到,不代表浏览器真执行了隔离。真正有没有效,得看渲染行为本身。
三个靠谱的验证方法:
- 打开 Chrome DevTools → Rendering 面板 → 勾选 “Paint Flashing”,滚动或更新时,闪光应严格限制在目标容器内部
- 勾选 “Layout Shift Regions”,如果折叠/展开区域周围不再出现大片红色高亮,说明 `layout` 隔离成功了
- Performance 面板录制操作,对比前后 “Layout” 和 “Update Layer Tree” 时间有明显下降,才证明隔离真正起效
最常被忽略的是尺寸约束——哪怕只是加了 `min-height: 0`,也比不写强;而最容易误用的是 `contain: strict`,它在多层定位嵌套中极易引发塌陷或错位,除非你已经确认容器尺寸完全可控,且子元素不参与文档流高度计算。