CSS 砌体的替代建议

发布时间:2024 年 4 月 30 日;上次更新时间:2026 年 2 月 13 日

Chrome 团队非常希望看到在 Web 上实现 Masonry 类型布局。不过,我们认为,如果按照最近的 WebKit 博文中的提议,将其作为 CSS 网格规范的一部分来实现,那将是一个错误。我们还认为,WebKit 帖子反对的是无人提议的 masonry 版本。

因此,本文旨在说明 Chrome 团队为何对将 Masonry 实现为 CSS 网格布局规范的一部分感到担忧,并明确说明替代方案的具体作用。简而言之:

  • Chrome 团队非常希望解除对 Masonry 的限制,我们知道这是开发者想要的功能。
  • 将 Masonry 添加到网格规范中存在问题,原因不仅仅在于您是否认为 Masonry 是网格。
  • 在网格规范之外定义砌块不会阻止砌块使用多个轨道大小,也不会阻止使用对齐或间隙等属性,或网格布局中使用的任何其他功能。

砌块是否应成为网格的一部分?

Chrome 团队认为,Masonry 应该是一种单独的布局方法,使用 display: masonry(或另一个更合适的关键字,如果确定了更好的名称)进行定义。在本文的后半部分,您可以查看一些代码示例,了解这在代码中会是什么样子。

我们认为,在网格布局之外更好地定义 Masonry 有两个相关的原因:布局性能问题和 Masonry 与网格都有一些功能,这些功能在一个布局方法中很有意义,但在另一个布局方法中却不然。

性能

在浏览器处理尺寸和放置方式方面,网格和瀑布流是相反的。在布局网格时,所有项都会在布局之前放置,并且浏览器会确切地知道每个轨道中的内容。这样便可实现网格中非常实用的复杂内在尺寸调整。使用 Masonry 时,系统会按布局放置项,浏览器不知道每个轨道中有多少项。这并非所有内在大小轨道或所有固定大小轨道的问题,而是混合使用固定大小轨道和内在大小轨道时的问题。为了解决此问题,浏览器需要执行一个预布局步骤,以每种可能的方式布局每个项目来获取测量结果,对于大型网格,这会导致布局性能问题。

因此,如果您有一个轨道定义为 grid-template-columns: 200px auto 200px 的 masonry 布局(这是网格中非常常见的做法),就会开始遇到问题。添加子网格后,这些问题会呈指数级增长

有人认为,大多数人不会遇到这个问题,但我们已经知道,确实有人拥有非常大的网格。我们不希望发布使用方式受限的产品,因为有替代方法。

对于每种布局方法中不合理的地方,我们该怎么做?

当 flexbox 和网格成为 CSS 的一部分时,开发者经常会觉得它们的行为不一致。他们遇到的不一致问题是由于长期以来基于块布局对布局工作方式的假设造成的。 随着时间的推移,开发者开始了解格式设置上下文。当我们切换到网格或弹性格式设置上下文时,某些行为会有所不同。例如,您知道在 flexbox 中,并非所有对齐方法都可用,因为 flexbox 是一维的。

将 Masonry 捆绑到网格中会破坏格式设置上下文与对齐属性等内容可用性之间的清晰关联,这些内容在 Box Alignment 规范中是按格式设置上下文定义的。

如果我们决定通过禁止在 masonry 中使用混合的固有轨道和固定轨道定义来解决之前概述的性能问题,那么您必须记住,一种非常常见的网格布局模式不适用于 masonry。

还有一些模式在 Masonry 中有意义,例如 grid-template-columns: repeat(auto-fill, max-content),因为您没有交叉约束,但在网格中需要保持无效。以下是我们预计行为会有所不同或具有不同有效值的属性列表。

  • grid-template-areas:在 Masonry 中,您只能指定非 Masonry 方向的初始行。
  • grid-template:简写形式需要考虑所有差异。
  • 由于合法值存在差异,因此跟踪 grid-template-columnsgrid-template-rows 的尺寸调整值。
  • grid-auto-flow 不适用于 Masonry,masonry-auto-flow 不适用于网格。合并它们会因您所处的布局方法而导致无效问题。
  • 网格有四个放置属性(grid-column-start 等),而 Masonry 只有两个。
  • 网格可以使用所有六个 justify-*align-* 属性,但 Masonry 仅使用一部分属性,与 Flexbox 类似。

此外,还需要指定在所有新错误情况下(由开发者使用在网格(含 Masonry)或网格(不含 Masonry)中无效的值导致)会发生什么情况。例如,可以使用 grid-template-columns: masonrygrid-template-rows: masonry,但不能同时使用这两者。如果您同时使用这两种方法,会发生什么情况? 必须指定这些详细信息,以便所有浏览器执行相同的操作。

从规范的角度来看,无论是现在还是将来,这一切都会变得非常复杂。我们需要确保所有内容都考虑到了 Masonry,以及它在 Masonry 中是否有效。从开发者的角度来看,这也令人困惑。为什么您需要记住,尽管使用了 display: grid,但由于使用了 masonry,某些功能无法正常运行?

替代方案

如前所述,Chrome 团队希望在网格规范之外定义 Masonry。这并不意味着它仅限于列大小相同的非常简单的布局方法。WebKit 帖子中的所有演示仍可实现。

经典砌体布局

大多数人想到砌体时,都会想到具有多个等宽列的布局。这可以使用以下 CSS 来定义,与等效的捆绑网格版本相比,所需的代码行数更少。

.masonry {
  display: masonry;
  masonry-template-tracks: repeat(auto-fill, minmax(14rem, 1fr));
  gap: 1rem;
}

轨道大小相等。

使用网格轨道尺寸调整功能来设置不同的列宽

除了之前提到的混合使用固有轨道大小和固定轨道大小的问题之外,您还可以使用网格中您喜爱的所有轨道大小。例如,WebKit 博文中的示例,即窄列和宽列交替重复的模式。

.masonry {
  display: masonry;
  masonry-template-tracks: repeat(auto-fill, minmax(8rem, 1fr) minmax(16rem, 2fr)) minmax(8rem, 1fr);
  gap: 1rem;
}

宽轨道和窄轨道交替的模式。

用于 Masonry 的其他轨道尺寸

还有一些轨道尺寸调整选项,由于网格是一种二维布局方法,因此我们不允许在网格中使用这些选项。这些属性在 Masonry 中非常有用,但如果它们在网格中不起作用,就会令人困惑。

自动填充 max-content 大小的轨道。

.masonry {
  display: masonry;
  masonry-template-tracks: repeat(auto-fill, max-content);
  gap: 1rem;
}

自动填充 auto 大小的轨道,这将创建大小相同的轨道,并自动调整大小以适应最大的轨道。

.masonry {
  display: masonry;
  masonry-template-tracks: repeat(auto-fill, auto);
  gap: 1rem;
}

具有自动调整大小的轨道的分块布局。

允许内容跨列,并将项放置在砌块布局上

没有理由不在单独的 Masonry 规范中让内容跨列显示。这可能会使用 masonry-track 属性,它是 masonry-track-startmasonry-track-end 的简写形式,因为在网格布局中,您只有一个维度来跨越事物。

.masonry {
  display: masonry;
  masonry-template-tracks: repeat(auto-fill, auto);
}

.span-2 {
  masonry-track: span 2; /* spans two columns */
}

.placed {
  masonry-track: 2 / 5; /* covers tracks 2, 3, and 4 */
}

包含放置和跨列项的 Masonry。

采用 masonry 轨道的子砌块或子网格

这可以通过单独的 Masonry 规范来支持,但前提是禁止使用混合的固有大小轨道和固定大小轨道。具体情况需要进一步明确。我们认为这应该没问题。

总结

我们希望能够制定出可互操作的规范。不过,我们希望以一种在当前和未来都能良好运行的方式来实现这一点,并且这种方式能够让开发者信赖。解决上述性能问题的唯一方法是使第二个问题(即在 Masonry 中存在部分非法网格)变得更严重。我们认为这不是一个好的解决方案,尤其是在可以保留所有想要的网格功能,同时清晰区分不同之处的情况下。

如果您有任何反馈,请加入问题 9041 中的讨论。

感谢 Bramus、Tab Atkins-Bittner、Una Kravets、Ian Kilpatrick 和 Chris Harrelson 对本文的审核以及为本文提供信息的讨论。