使用 CSS 包含属性帮助浏览器进行优化

已发表: 2022-03-10
快速总结 ↬ CSS contain属性为您提供了一种向浏览器解释布局的方法,因此可以进行性能优化。 但是,它确实会在您的布局方面产生一些副作用。

在本文中,我将介绍一个刚刚成为 W3C 推荐标准的 CSS 规范。 CSS contain规范定义了一个属性, contains ,它可以帮助您向浏览器解释布局的哪些部分是独立的,并且如果布局的其他部分发生更改,则不需要重新计算。

虽然存在此属性是出于性能优化的原因,但它也会影响页面的布局。 因此,在本文中,我将解释您可以从中受益的不同类型的包含,以及在将contain应用于站点中的元素时需要注意的事项。

布局重新计算的问题

如果您正在构建简单的网页,在使用 JavaScript 加载元素后不会动态添加或更改元素,那么您无需担心 CSS Containment 解决的问题。 加载页面时,浏览器只需要计算一次布局。

当您想在页面中添加元素而不需要用户重新加载它时,包含变得有用的地方。 在我的示例中,我创建了一个大事件列表。 如果单击按钮,则修改第一个事件,添加一个浮动元素,并更改文本:

带有按钮的项目列表,用于更改第一项中的某些内容
(参见 CodePen 上的初始示例)

当我们的盒子的内容发生变化时,浏览器必须考虑到任何元素都可能发生了变化。 浏览器通常很擅长处理这个问题,因为这是经常发生的事情。 也就是说,作为开发人员,您将知道每个组件是否是独立的,并且对一个组件的更改不会影响其他组件,因此如果您可以通过 CSS 让浏览器知道这一点,那就太好了。 这就是包含和 CSS contain属性为您提供的内容。

跳跃后更多! 继续往下看↓

遏制如何提供帮助?

HTML 文档是一种树形结构,您可以在使用 DevTools 检查任何元素时看到它。 在上面的示例中,我确定了一项我想使用 JavaScript 更改的项目,然后对内部进行一些更改。 (这意味着我只更改该列表项的子树内的内容。)

扩展了特色项目的列表项以查看其中的元素的 DevTools
在 DevTools 中检查列表项

contain属性应用于一个元素会告诉浏览器,更改的范围是该元素的子树,以便浏览器可以进行任何可能的优化 - 安全地知道该元素之外的任何其他内容都不会改变。 特定浏览器可能会做什么取决于引擎。 CSS 属性只是让你——作为这个布局的开发者和专家——有机会让它知道。

在许多情况下,您可以安全地继续使用contain属性,但是,不同的值会带来一些潜在的副作用,在将属性添加到站点中的元素之前值得了解这些副作用。

使用遏制

contains 属性可以设置三种不同类型的contain

  • layout
  • paint
  • size

注意2 级规范中有一个style值。 它已从级别 1 中删除,因此未出现在建议中,也未在 Firefox 中实现。

布局

布局遏制带来了最大的好处。 要打开布局包含,请使用以下代码段:

 .item { contain: layout; }

启用布局包含后,浏览器知道元素外部的任何内容都不会影响内部布局,元素内部的任何内容都无法更改其外部事物的布局。 这意味着它可以针对这种情况进行任何可能的优化。

启用布局包含时会发生一些额外的事情。 这些都是确保此框和内容独立于树的其余部分的所有内容。

该框建立了一个独立的格式化上下文。 这确保了盒子的内容保留在盒子中——特别是浮动将被包含在内,并且边距不会在盒子中塌陷。 这与我们在使用display: flow-root时打开的行为相同,如我的文章“理解 CSS 布局和块格式化上下文”中所述。 如果浮动可能会从您的盒子中伸出,导致后续文本在浮动周围流动,那将是元素正在改变其外部事物的布局的情况,使其成为包含的不良候选者。

包含框充当任何绝对或固定位置后代的包含块。 这意味着它的行为就像您在已应用的框上使用了position: relative一样contain: layout

该框还创建了一个堆叠上下文。 因此z-index将对这个元素起作用,它的子元素将基于这个新的上下文进行堆叠。

如果我们看一下这个例子,这次是contain: layout ,你可以看到当浮动元素被引入时,它不再伸出盒子的底部。 这是我们新的块格式化上下文,包含浮动。

项目列表,浮动元素包含在父框的边界内
使用包含:布局包含浮动(参见 CodePen 上的布局包含示例)

要打开油漆遏制,请使用以下命令:

 .item { contain: paint; }

启用绘制包含后,会出现与布局包含相同的副作用:包含框成为独立的格式化上下文,定位元素的包含块,并建立堆叠上下文。

绘制包含的作用是向浏览器指示包含块内的元素在该框的边界之外将不可见。 内容基本上将被剪辑到框中。

我们可以通过一个简单的例子看到这种情况。 即使我们给我们的卡片一个高度,浮动的项目仍然会从盒子的底部伸出,因为浮动是不流动的。

一个从容器底部伸出的浮动盒子
浮动不包含在列表项中

启用油漆控制后,浮动项目现在被裁剪为框的大小。 没有任何东西可以在元素边界之外绘制contain: paint

一个盒子,里面有一个浮动的盒子,它在脱离盒子的地方被切断了
盒子的内容被裁剪到盒子的高度(参见 CodePen 上的绘制示例)

尺寸

如果您不完全了解它的工作原理,则尺寸限制是最有可能给您带来问题的值。 要应用大小限制,请使用:

 .item { contain: size; }

如果你使用尺寸限制,那么你就是在告诉浏览器你知道盒子的尺寸并且它不会改变。 这确实意味着,如果您有一个在块维度中自动调整大小的框,它将被视为内容没有大小,因此框会像没有内容一样塌陷。

在下面的示例中,我没有给li指定高度; 它们还contain: size 。 您可以看到所有项目都已折叠,就好像它们根本没有内容一样,这使得列表看起来非常奇特!

带有按钮的项目列表,用于更改第一项中的某些内容
(参见 CodePen 上的尺寸示例)

如果你给盒子一个高度,那么在使用contain: size时高度将被尊重。 单独而言,大小包含不会创建新的格式化上下文,因此不会像布局和绘画包含那样包含浮动和边距。 您不太可能单独使用它; 相反,您很可能会将它与 contains 的其他值一起应用,以获得最大可能的contain

速记值

在大多数情况下,您可以使用两个速记值之一来获得最佳的遏制效果。 要打开布局和绘制包含,请使用contain: content; , 并打开所有可能的包含(请记住,没有大小的项目将折叠),请使用contain: strict

规范说:

contain: content可以合理地“安全”地广泛应用; 它在实践中的影响相当小,大多数内容都不会违反它的限制。 但是,因为它不应用大小限制,所以元素仍然可以响应其内容的大小,这可能导致布局无效在树上比预期的更远。 尽可能使用contain: strict来获得尽可能多的遏制。”

因此,如果您事先不知道项目的大小,并且了解浮动和边距将被包含的事实,请使用contain: content 。 如果您确实知道项目的大小并且对遏制的其他副作用感到满意,请使用contain: strict 。 剩下的就是浏览器了,你已经解释了你的布局是如何工作的。

我现在可以使用收容措施吗?

CSS Containment 规范现在是 W3C 推荐标准,我们有时将其称为Web 标准。 为了使规范进入这个阶段,我们需要在 Firefox 和 Chrome 中看到该功能的两种实现:

Can I Use 上有关 Containment 的浏览器支持信息的屏幕截图
浏览器对遏制的支持(来源:我可以使用)

由于此属性对用户是透明的,因此即使您在不支持它的浏览器中有大量访问者,添加到任何站点也是完全安全的。 如果浏览器不支持遏制,那么访问者将获得他们通常获得的体验,支持浏览器的访问者将获得增强的性能。

我建议添加到您在组件或模式库中创建的任何组件中是一件很棒的事情,如果您以这种方式工作,那么每个组件很可能被设计为一个独立的东西,不会影响其他元素页面,使contain: content成为有用的补充。

因此,如果您有一个在加载后向 DOM 添加内容的页面,我建议您尝试一下——如果您得到任何有趣的结果,请在评论中告诉我!

相关资源

以下资源将为您提供有关遏制实施和潜在性能优势的更多详细信息:

  • contain CSS 属性”, MDN 网络文档
  • “Chrome 52 中的 CSS 包含”,谷歌开发者
  • “CSS 包含模块级别 1”, W3C 建议
  • “CSS 遏制简介”, Manuel Rego Casasnovas