注:文章的“砖石”指 “masonry”,“网格”指 “grid”,分别是两种布局设计。
Chrome 团队渴望在Web上实现 masonry
(砖石)类型的布局。然而,我们认为,按照最近WebKit帖子中提出的方式,将其作为CSS Grid规范的一部分来实现将是一个错误。我们还认为,WebKit帖子反对的砖石版本实际上并没有人提出。 因此,这篇文章旨在解释为什么我们Chrome团队对将砖石作为CSS Grid布局规范的一部分实现感到担忧,并阐明替代提案所提供的确切功能。简而言之:
- Chrome团队非常希望解锁砖石,我们知道这是开发人员想要的。
- 将砖石添加到网格规范中是有问题的,不仅仅是因为你认为砖石是一个网格或不是。
- 在网格规范之外定义砖石,并不妨碍为砖石使用多个轨道大小,或使用对齐或间隙等属性,或在网格布局中使用的任何其他功能。
砖石应该是网格的一部分吗?
Chrome团队认为,砖石应该是一种单独的布局方法,使用display: masonry
(或另一个关键词,如果有更好名字决定的话)来定义。在这篇文章的后面,你可以看到一些代码示例,展示它可能是什么样子。 我们认为砖石最好在网格布局之外定义,有两个相关的原因——潜在的布局性能问题,以及砖石和网格都有一些特性,在一个布局方法中有意义,而在另一个布局方法中没有意义。 注意:这篇文章试图突出核心问题,而不是给出每个问题的完整和详细的解释。访问相关的问题以获取详细信息。
性能
网格和砖石在浏览器处理大小和位置方面是相反的。当网格布局时,所有项目在布局之前就已放置,浏览器确切地知道每个轨道中有什么。这使得网格中非常有用的复杂内在尺寸成为可能。对于砖石,项目在布局时放置,浏览器不知道每个轨道中有多少个。如果你混合了固定和内在尺寸的轨道,这不是一个问题,但是如果这样做,浏览器需要做一个预布局步骤,以测量每个项目在每种可能的方式中的布局,对于一个大的网格,这将有助于布局性能问题。 因此,如果你有一个砖石布局,轨道定义为grid-template-columns: 200px auto 200px
——在网格中非常常见的做法——你开始遇到问题。这些问题一旦添加了子网格,就会变得指数级增长。 有一种观点认为,大多数人不会遇到这个问题,但我们已经知道人们确实有非常大的网格。我们不想发布一些有限制的东西,当有另一种方法时。
我们如何处理在每种布局方法中没有意义的事情?
当flexbox和grid成为CSS的一部分时,开发人员经常感到它们的行为不一致。他们所经历的不一致性是因为基于块布局的长期假设,对布局的工作方式有长期的假设。随着时间的推移,开发人员已经开始理解格式化上下文。当我们切换到网格或flex格式化上下文时,一些事情的行为会有所不同。例如,你知道当你在flexbox中时,并不是所有的对齐方法都是可用的,因为flexbox是一维的。 将砖石捆绑到网格中打破了格式化上下文和对齐属性等事物的可用性之间的明确联系,这些属性在每个格式化上下文中都定义在Box Alignment规范中。 如果我们决定通过使混合内在和固定轨道定义在砖石中非法来处理前面概述的性能问题,那么你必须记住,对于网格布局来说,一个非常常见的模式对砖石不起作用。 还有一些模式在砖石中有意义,例如grid-template-columns: repeat(auto-fill, max-content)
,因为你没有交叉约束,但需要保持在网格中无效。以下是我们期望行为不同或具有不同有效值的属性列表。
grid-template-areas
:在砖石中,你只能在非砖石方向上指定初始行。grid-template
:缩写需要考虑所有差异。grid-template-columns
和grid-template-rows
的轨道大小值,由于合法值的差异。grid-auto-flow
不适用于砖石,masonry-auto-flow
不适用于网格。将它们合并将创建由于你所处的布局方法而无效的事物的问题。- 网格有四个放置属性(和两个属性合并成一对,一个属性合并成全部四个),设置网格项目的每个边缘的位置。砖石只能设置这两个;另外两个由流程决定。这意味着当容器处于砖石模式时,我们必须忽略三个属性(两个单独的和一个对)和第四个的一部分(
grid-area
中的两个值,可能)。 - 网格可以使用所有六个
justify-*
和align-*
属性,但砖石只使用一个子集,像flexbox一样。 还将需要指定所有由开发人员使用在grid-with-masonry或grid-without-masonry中无效的值引起的新错误案例中会发生什么。例如,使用grid-template-columns: masonry
或grid-template-rows: masonry
是有效的,但不能同时使用两者。如果你同时使用两者会怎样?这些细节必须被指定,以便所有浏览器都做同样的事情。 从规范的角度来看,所有这些都变得复杂,现在和将来都是如此。我们需要确保一切考虑到砖石,以及它是否在砖石中工作。从开发人员的角度来看,这也令人困惑。为什么你需要记住,尽管使用display: grid
,但有些东西因为使用砖石而不起作用?
替代提案
正如已经提到的,Chrome团队希望在网格规范之外定义砖石。这并不意味着它将被限制为具有相同列大小的非常简单的布局方法。WebKit帖子中的所有演示仍然可能。 注意: 示例使用display: masonry
和masonry-*
属性,是否有masonry
是最好的名称使用,有一个单独的问题。
经典砖石布局
当大多数人想到砖石时,他们想到的是一个具有多个相等大小列的布局。这将使用以下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;
}
为砖石增加轨道大小
还有额外的轨道大小选项,我们不允许在网格中使用,因为网格是二维布局方法。这些在砖石中会很有用,但如果它们在网格中不起作用,那将令人困惑。
自动填充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-start
和masonry-track-end
的缩写,因为当你处于砖石布局中时,你只有一个维度可以跨越事物。
.masonry {
display: masonry;
masonry-template-tracks: repeat(auto-fill, auto);
}
.span-2 {
masonry-track: span 2; /* 跨越两列 */
}
.placed {
masonry-track: 2 / 5; /* 覆盖第2、3和4轨道 */
}
子砖石或子网格采用砖石轨道
这可以与单独的砖石规范一起支持,再次规定混合内在和固定大小的轨道是不允许的。确切的外观将需要定义。我们看不到为什么这不行。
结论
我们希望达到一个可以互操作地发货的规范的点。然而,我们希望以一种现在和将来都能很好地工作的方式做到这一点,并且可以被开发人员信赖。处理前面概述的性能问题的唯一方法,将是使第二个问题——使网格的一部分在砖石中非法——变得更糟。我们认为这不是一个好的解决方案,特别是当你可以在保持不同事物清晰分离的同时拥有你想要的所有网格特性时。
如果您有任何反馈,请加入Issue 9041的讨论。
感谢Bramus、Tab Atkins-Bittner、Una Kravets、Ian Kilpatrick和Chris Harrelson审查这篇文章和提供信息的讨论。