动态内容的渲染成本
每次 DOM 更改时,浏览器都必须重新计算样式、布局和绘制。对于具有许多交互式小部件(聊天提要、股票行情、无限滚动列表)的页面,这些重新计算可能会波及整个文档,导致卡顿和丢帧。
CSS contain 属性告诉浏览器子树与页面的其余部分独立。这使得引擎能够跳过全局重新计算并将元素视为孤岛。
包含值
该属性接受一个或多个以空格分隔的关键字:
| 价值 | 效果 |
|---|---|
| 占位符_0 | 任何外部元素都不能影响该子树的布局,反之亦然。该元素建立一个新的格式化上下文。 |
| 占位符_0 | 子树被剪切到元素的框内。后代永远不会在其范围之外进行渲染。启用绘制时间优化。 |
| 占位符_0 | 元素的大小由其指定的 width/height 决定,忽略子元素。对于虚拟化容器很有用。 |
| 占位符_0 | 包含反增量、引号和其他作用域属性。不太常用。 |
| 占位符_0 | layout paint size style 的简写 — 完整收容集。 |
| 占位符_0 | layout paint style 的简写 — 不包括 size,因此盒子仍然可以收缩包裹其子项。 |
遏制如何隔离渲染
如果没有包含,更改固定位置聊天小部件的 top 会触发布局重新计算,该计算向上传播到根,并可能向下传播到不相关的分支。
.chat-widget {
contain: layout paint size;
width: 320px;
height: 480px;
}
使用 contain: layout,浏览器知道该子树不会导致布局移动到其框之外。 布局步骤在容器边界处停止。使用 contain: paint ,子像素不会渗出容器的边框框,因此重绘的范围可以限定在较小的脏矩形内。
现实世界的用例
第三方嵌入 - 嵌入 Twitter 时间线或地图小部件会引入未知的 DOM 突变。将它们包装在包含的 <div> 中可以防止它们的内部回流减慢主要内容的速度。
<div class="embed-container">
<div id="third-party-widget"></div>
</div>
.embed-container {
contain: content;
width: 100%;
max-width: 600px;
}
虚拟滚动 - 像react-window这样的库在视口容器上设置显式尺寸。添加 contain: strict 可以强化尺寸约定,并让浏览器优化剪裁子项的绘制。
动画元素 - 连续动画 UI 元素(例如,加载旋转器)会触发每帧重绘。包含它会将这些重绘与页面的其余部分隔离开来。
性能影响
具有 10,000 个 DOM 节点的页面的典型基准测试显示:
| 场景 | 风格重新计算 | 布局时间 |
|---|---|---|
| 无遏制 | 〜12 毫秒 | ~8 毫秒 |
| 占位符_0 | 〜2 毫秒 | ~1 毫秒 |
| 占位符_0 | 〜2 毫秒 | <1 毫秒 |
随着小部件数量的增加,这种改进会更加复杂。对于复杂的仪表板应用程序,遏制可以将总帧时间减少 30-50%。
浏览器支持
所有现代浏览器都支持 contain:
- 铬 52+
- 火狐浏览器 69+
- Safari 15.4+
- 边缘 79+
content 和 strict 关键字在属性本身受支持的任何地方都会被识别。 style 关键字较新,但广泛可用。
注意事项
contain: size需要显式尺寸。 如果没有设置width/height,元素会折叠为 0px。始终将size与定义的大小配对。- **遏制不是性能银弹。**在小元素上过度使用它会增加开销。将其应用于独立的、突变较多的子树。
contain: style很少需要。 它的范围是反增量和列表项标记,但不隔离 CSS 自定义属性。
概括
CSS 包含是一种低成本、高影响力的性能工具,适用于任何具有动态内容岛的页面。通过有选择地隔离子树,您可以防止不必要的布局和绘制工作,从而保持交互流畅和高帧速率。从小部件和嵌入上的 contain: content 开始,然后进行分析以查看差异。

