CSS 砌体的替代建议

Chrome 团队非常期待在 Web 上实现砖块砌图类型的布局。不过,我们认为,按照近期的 WebKit 博文中提出的建议,将其作为 CSS Grid 规范的一部分来实现是错误的。我们还认为,WebKit 博文反对的功能版本是从未有人提出的。

因此,本文旨在说明 Chrome 团队为何对在 CSS 网格布局规范中实现砖块布局存有疑虑,并明确说明备选方案究竟能实现什么。简而言之:

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

是否应将“砖块”作为网格的一部分?

Chrome 团队认为,砖块砌图应是一种单独的布局方法,使用 display: masonry(或在确定更好的名称后使用其他关键字)进行定义。在本文的后面部分,您可以看到一些示例,了解这在代码中的具体表现形式。

我们认为在网格布局之外定义图块布局更为合适,原因有两个:布局性能问题的可能性,以及图块布局和网格布局都有在一种布局方法中适用但在另一种布局方法中不适用的功能。

性能

在浏览器处理尺寸和放置方式方面,网格布局和砖块布局截然不同。在网格布局时,所有项都会放置在布局之前,并且浏览器确切知道每个轨道中的内容。这支持在网格中非常有用的复杂内在尺寸。使用“砖块”模式时,系统会按布局方式放置项,并且浏览器不知道每个轨道中有多少项。这对所有采用内在大小的轨道或所有采用固定大小的轨道来说都不是问题,但如果您混合使用固定轨道和内在轨道,则会出现此问题。为了解决此问题,浏览器需要执行布局前步骤,以尽可能多的方式布局每个项以获取测量结果,如果网格较大,这会导致布局性能问题。

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

有人认为大多数用户不会遇到这种情况,但我们已经知道,用户确实会使用非常大的网格。如果有替代方法,我们不希望发布在使用方式上有限制的功能。

对于每个布局方法中不合理的内容,我们该怎么办?

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

将砖块排列方式打包到网格中会打破格式上下文与对齐属性等可用性之间的这种明确关联,这些属性在“盒子对齐”规范中根据格式上下文进行定义。

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

还有一些模式在砖块砌图中很有用,例如 grid-template-columns: repeat(auto-fill, max-content),因为您没有交叉约束条件,但需要在网格中保持无效状态。以下是我们预计会表现出不同行为或具有不同有效值的属性列表。

  • grid-template-areas:在砖块砌图中,您只能指定非砖块砌图方向的初始行。
  • grid-template:缩写形式需要考虑所有差异。
  • 由于法定值不同,请跟踪 grid-template-columnsgrid-template-rows 的大小值。
  • grid-auto-flow 不适用于“砖块砌图”,masonry-auto-flow 不适用于“网格”。合并它们会导致由于您所使用的布局方法而出现无效问题。
  • 网格有四个展示位置属性(grid-column-start 等),而砖块砌图只有两个。
  • 网格可以使用所有 6 个 justify-*align-* 属性,但砖石布局仅使用 flexbox 等部分属性。

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

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

备选方案

如前所述,Chrome 团队希望在网格规范之外定义砖块排列。这并不意味着它只能采用列大小完全相同的非常简单的布局方法。WebKit 博文中的所有演示仍可正常运行。

传统砌体布局

提到“砖块砌图”,大多数人都会想到包含多个大小相同的列的布局。这将使用以下 CSS 进行定义,与等效的网格捆绑版本相比,所需的代码行数更少。

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

大小相同的轨道。

针对不同的列宽使用网格类型轨道大小

除了前面提到的混合使用内在尺寸和固定轨道尺寸时会出现的问题外,您可以使用 Grid 中的所有轨道尺寸。例如 WebKit 博文中的示例,重复的窄列和宽列模式。

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

宽窄相间的轨道图案。

为图块砌体模式添加了其他轨道大小

由于网格是一种二维布局方法,因此我们不允许在网格中使用其他轨道大小选项。这些功能在砖块布局中很有用,但如果在网格布局中不起作用,就会令人困惑。

自动填充 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-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 */
}

包含放置项和跨项的砖块砌图。

采用砖块排列轨道的子砖块排列或子网格

这可以通过单独的图块排列规范来支持,但前提是禁止混合使用内在轨道和固定大小的轨道。需要定义具体内容。我们认为这样做应该没有问题。

总结

我们希望能够实现可互操作的规范。不过,我们希望以一种既适用于现在又适用于未来且可供开发者信赖的方式来实现这一点。要解决上述性能问题,唯一的方法是让第二个问题(即在砖块砌图中网格的某些部分是非法的)变得更严重。我们认为这不是一个好解决方案,尤其是在您可以同时拥有所需的所有网格功能,同时将不同内容明确分开的情况下。

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

感谢 Bramus、Tab Atkins-Bittner、Una Kravets、Ian Kilpatrick 和 Chris Harrelson 审核这篇文章并参与相关讨论。