Featured image of post 通过 CSS 包含属性隔离布局计算Featured image of post 通过 CSS 包含属性隔离布局计算

通过 CSS 包含属性隔离布局计算

使用 CSS 包含提高大量更新的动态元素的渲染性能。

动态内容的渲染成本

每次 DOM 更改时,浏览器都必须重新计算样式、布局和绘制。对于具有许多交互式小部件(聊天提要、股票行情、无限滚动列表)的页面,这些重新计算可能会波及整个文档,导致卡顿和丢帧。

CSS contain 属性告诉浏览器子树与页面的其余部分独立。这使得引擎能够跳过全局重新计算并将元素视为孤岛。

包含值

该属性接受一个或多个以空格分隔的关键字:

价值效果
占位符_0任何外部元素都不能影响该子树的布局,反之亦然。该元素建立一个新的格式化上下文。
占位符_0子树被剪切到元素的框内。后代永远不会在其范围之外进行渲染。启用绘制时间优化。
占位符_0元素的大小由其指定的 width/height 决定,忽略子元素。对于虚拟化容器很有用。
占位符_0包含反增量、引号和其他作用域属性。不太常用。
占位符_0layout paint size style 的简写 — 完整收容集。
占位符_0layout 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+

contentstrict 关键字在属性本身受支持的任何地方都会被识别。 style 关键字较新,但广泛可用。

注意事项

  • contain: size 需要显式尺寸。 如果没有设置 width/height,元素会折叠为 0px。始终将 size 与定义的大小配对。
  • **遏制不是性能银弹。**在小元素上过度使用它会增加开销。将其应用于独立的、突变较多的子树。
  • contain: style 很少需要。 它的范围是反增量和列表项标记,但不隔离 CSS 自定义属性。

概括

CSS 包含是一种低成本、高影响力的性能工具,适用于任何具有动态内容岛的页面。通过有选择地隔离子树,您可以防止不必要的布局和绘制工作,从而保持交互流畅和高帧速率。从小部件和嵌入上的 contain: content 开始,然后进行分析以查看差异。