CSS 砌体布局之争:Grid 应进化还是让位新模块?

本文探讨了CSS中实现砌体布局的三种提案:扩展Grid规范、独立砌体模块及苹果WebKit的Item Flow方案,分析了各自的技术实现、优缺点及对前端开发的影响。

CSS 中的砌体布局:Grid 应进化还是为新模块让路?

近期,CSS 中新增砌体式布局支持的提案出现分歧。一方提议扩展现有的 CSS Grid 规范,另一方则建议将砌体布局设为独立模块。而最近,苹果 WebKit 团队提出了第三种方案——“Item Flow”。本文将详细探讨这三种方案。

如果你需要构建类似 Pinterest 的布局,但厌倦了 JavaScript,CSS 是否终于能提供解决方案?初看 Pinterest 的页面,你可能会认为 CSS 网格布局就足够了,但实际构建时才会发现仅靠 display: grid 和一些调整是远远不够的。事实上,Pinterest 使用 JavaScript 构建其布局,但如果仅用 CSS 实现,那该多酷?

也许有办法。CSS 网格布局有一个实验性的 grid-template-rows: masonry 值。砌体布局是一种不规则的流动网格。不规则之处在于,砌体布局的下一行项目会上升填充砌体轴上的空间,而不是遵循严格的网格模式并在较短片段后留下空间。它是作品集、图片库和社交信息流的理想选择——这些设计依赖于有机流动。但问题是:虽然这个实验性功能存在(例如在启用标志的 Firefox Nightly 中),但由于有限的浏览器支持和当前形式的一些粗糙边缘,它并不是你期望的无缝解决方案。

也许没有。CSS 缺乏原生砌体支持,迫使开发者使用 hack 或 JavaScript 库(如 Masonry.js)。有良好设计背景的开发人员对 CSS 网格形式的砌体布局提出了批评,例如 Rachel 指出砌体的有机流动与 Grid 的严格二维结构形成对比,可能会让期望 Grid 类似行为的开发者感到困惑;Ahmad Shadeed 抱怨它使网格布局变得比应有的更复杂,可能会让重视 Grid 清晰结构布局的开发者不知所措。Geoff 也附和 Rachel Andrew 的担忧,即“为了理解砌体行为而教授和学习 Grid,不必要地将两种不同的格式化上下文混为一谈”,使依赖清晰心智模型的设计师和开发者的教育复杂化。

也许还有希望。苹果 WebKit 团队刚刚提出了一个新的竞争者,声称不仅将 Grid 和砌体的优点合并为一个统一的系统简写,还包括了 Flexbox 的概念。想象一下,三个 CSS 布局系统的精华合而为一。

鉴于这些抱怨和批评——以及一个新的参与者——问题是:CSS Grid 应该扩展以处理砌体,还是应该由一个全新的专用模块接管,或者 Item Flow 应该接手?

当前 CSS 中砌体的状态

许多开发者尝试使用 CSS Grid 配合手动行跨度 hack、CSS 列和 JavaScript 库来在其 Web 应用中实现砌体布局。没有原生砌体,开发者通常转向像这样的 Grid hack:一个 grid-auto-rows 技巧配合 JavaScript 来模拟流动。它有效——某种程度上——但裂缝很快显现。

例如,下面的示例依赖 JavaScript 在渲染后测量每个项目的高度,计算项目应跨度的 10px 行数(加上间隙),动态设置 grid-row-end,并使用事件监听器在页面加载和窗口调整大小时调整布局。

1
2
3
4
5
6
<div class="masonry-grid">
  <div class="masonry-item"><img src="image1.jpg" alt="Image 1"></div>
  <div class="masonry-item"><p>Short text content here.</p></div>
  <div class="masonry-item"><img src="image2.jpg" alt="Image 2"></div>
  <div class="masonry-item"><p>Longer text content that spans multiple lines to show height variation.</p></div>
</div>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.masonry-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); /* 响应式列 */
  grid-auto-rows: 10px; /* 小行高用于精确跨度 */
  grid-auto-flow: column; /* 从左到右填充列 */
  gap: 10px; /* 项目之间的间距 */
}

.masonry-item {
  /* 确保内容不溢出 */
  overflow: hidden;
}

.masonry-item img {
  width: 100%;
  height: auto;
  display: block;
}

.masonry-item p {
  margin: 0;
  padding: 10px;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
function applyMasonry() {
  const grid = document.querySelector('.masonry-grid');
  const items = grid.querySelectorAll('.masonry-item');

  items.forEach(item => {
    // 重置任何先前的跨度
    item.style.gridRowEnd = 'auto';

    // 根据项目高度计算要跨度的行数
    const rowHeight = 10; 
    const gap = 10; 
    const itemHeight = item.getBoundingClientRect().height;
    const rowSpan = Math.ceil((itemHeight + gap) / (rowHeight + gap));

    // 应用跨度
    item.style.gridRowEnd = `span ${rowSpan}`;
  });
}

// 在加载和调整大小时运行
window.addEventListener('load', applyMasonry);
window.addEventListener('resize', applyMasonry);

这个 Grid hack 让我们接近砌体布局——项目堆叠,间隙填充,看起来足够体面。但说实话:它还没有达到。上面的代码示例,与原生 grid-template-rows: masonry(这是实验性的,仅存在于 Firefox Nightly)不同,依赖 JavaScript 计算跨度,违背了“无 JavaScript”的梦想。JavaScript 逻辑通过在调整大小或内容更改时重新计算跨度来工作。正如 Chris Coyier 在对类似 hack 的批评中指出的,这可能导致复杂页面上的延迟。

此外,逻辑 DOM 顺序可能与视觉流不匹配,这是 Rachel Andrew 对砌体布局普遍提出的担忧。最后,如果图像加载缓慢或内容移位(例如,懒加载媒体),跨度需要重新计算,有布局跳动的风险。这并不是理想的 hack;我相信你会同意。

开发者需要流畅的体验,从人体工程学角度来说,用脚本 hack Grid 是一种心理杂耍。它迫使你在 CSS 和 JavaScript 之间切换来调整布局。一个原生解决方案,无论是 Grid 驱动的还是新模块,都必须实现轻松响应、整洁渲染和不破坏工具的工作流程。

“这就是为什么这场辩论很重要——我们的日常工作需要它。

选项 1:扩展 CSS Grid 以支持砌体

一种前进的方式是增强 CSS Grid 的砌体能力。截至本文撰写时,CSS 网格已扩展以容纳砌体。grid-template-rows: masonry 是 CSS Grid Level 3 的草案,目前正在 Firefox Nightly 中进行实验。此布局的列将保持为网格轴,而行则采用砌体。然后,子元素沿着行逐个项目布局,就像网格布局的自动放置一样。使用此布局,项目垂直流动,尊重列轨道但不尊重行约束。

此选项将 Grid 保留为首选布局系统,但允许它处理我们渴望的流动、间隙填充堆栈。

1
2
3
4
5
6
.masonry-grid {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  grid-template-rows: masonry;
}

首先,grid-masonry 风格建立在 CSS Grid 的熟悉性和强大工具(例如,DevTools 支持)之上。作为前端开发者,你可能已经玩过 grid-template-columnsgrid-area,所以你已经在学习矩阵中走了一半。砌体仅扩展了现有功能,消除了从头学习全新语法的需要。此外,Grid 的强大工具随 Chrome DevTools 的网格覆盖或 Firefox 的布局检查器而来,消除了对 JavaScript hack 的需求。

别太快:有限制。Grid 的规范已经包括 align-contentgrid-auto-flow 等属性。将砌体堆叠在列表中风险将其变成迷宫。

然后还有边缘情况。当你希望一个项目跨多个列并流动砌体风格时会发生什么?或者当项目之间的间隙在列之间不对齐时?规范在这里仍然模糊,早期测试暗示了错误,例如如果内容动态加载,项目会不可预测地跳跃。这个问题可能会破坏布局,尤其是在响应式设计上。浏览器兼容性问题也存在。它仍然是实验性的,即使有 polyfills,它也不能在除 Firefox Nightly 以外的其他浏览器上工作。不是你会在下一个客户项目中尝试的东西,对吧?

选项 2:独立的砌体模块

如果我们有一个 display: masonry 方法呢?请容忍我几分钟。这不仅仅是 wishful thinking。早期的 CSS 工作组聊天已经提出了这个想法,值得描绘它如何改善布局。让我们深入探讨愿景、它可能如何工作,以及在此过程中的得失。

想象一个布局系统,不依赖 Grid 的刚性轨道或 Flexbox 的线性流,而是在垂直堆叠中蓬勃发展,带有水平扭曲。目标?为砌体的标志性外观提供一个干净的石板:项目级联向下列,自然填充间隙,无需 hack。受 CSSWG 讨论中的低语和 Chrome 团队的替代提案的启发,这个模块将优先考虑流动性 over 结构,为设计师提供一个感觉直观的工具,就像他们追逐的布局一样。想想 Pinterest 但没有 JavaScript 脚手架。

这是推销:一个名为 masonry 的显示值启动一个基于流的系统,其中项目默认垂直堆叠,水平调整以适应容器。你可以用以下简单属性控制方向和间距:

1
2
3
4
5
.masonry {
  display: masonry;
  masonry-direction: column;
  gap: 1rem;
}

想要更多控制?假设的额外功能如 masonry-columns: auto 可以模仿 Grid 的 repeat(auto-fill, minmax()),而 masonry-align: balance 可能会平衡列长度以获得抛光外观。它 less about 精确放置(Grid 的优势)and more about 让内容呼吸和流动,适应任何屏幕大小。这里的巨大胜利是与 Grid 的严格顺序 clean break。一个独立的模块保持它们 distinct:Grid 用于顺序,Masonry 用于流动。不再与不太合适的 Grid 属性搏斗;你得到一个为工作量身定制的系统。

当然,并非一帆风顺。一个全新的规范意味着从零开始。浏览器供应商需要团结 behind it,这可能很慢。此外,它可能导致选择 confusion,开发者会问:“我应该用 Grid 还是 Masonry 来做这个画廊?”但听我说:这个提议的模块可能会在澄清之前 muddy the waters,但 after the water is clear,它 safe for use by all and sundry。

Item Flow:统一的布局解决方案

2025 年 3 月,苹果的 WebKit 团队提出了 Item Flow,一个新系统,将 Flexbox、Grid 和砌体的概念统一到一组属性中。与其选择增强 Grid 或创建新的砌体模块,Item Flow 合并了它们的优势,用称为 item-flow 的简写替换了 flex-flowgrid-auto-flow。这个系统引入了四个长手属性:

  • item-direction:控制流动方向(例如,row、column、row-reverse)。
  • item-wrap:管理包裹行为(例如,wrap、nowrap、wrap-reverse)。
  • item-pack:确定包装密度(例如,sparse、dense、balance)。
  • item-slack:调整布局调整的容差,允许项目收缩或移位以适应。

Item Flow 旨在使砌体成为这些属性的自然结果,而不是一个单独的功能。例如,砌体布局可以通过以下方式实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
.container {
  display: grid; /* 或 flex */
  item-flow: column wrap dense;

  /* 长手版本 */
  item-direction: column;
  item-wrap: wrap;
  item-pack: dense;

  gap: 1rem;
}

此设置允许项目垂直流动,包裹成列,并紧密包装,模仿砌体的有机排列。密集包装选项,受 Grid 的 auto-flow: dense 启发,重新排序项目以最小化间隙,而 item-slack 可以微调间距以获得视觉平衡。

Item Flow 的承诺在于其广泛 use case。它通过像 Grid 的 nowrap 或 Flexbox 的平衡包装等功能增强了 Grid 和 Flexbox,解决了长期存在的开发者愿望清单。然而,该提案仍在讨论中,像 item-slack 这样的属性由于对非英语母语者的清晰度问题而面临命名辩论。

缺点?Item Flow 是一个面向未来的概念,截至 2025 年 4 月尚未在浏览器中实现。开发者必须等待标准化和采用,并且 CSS 工作组仍在收集反馈。

什么是正确的路径?

虽然没有直接答案,但砌体辩论取决于平衡简单性、性能和灵活性。用砌体扩展 Grid 是诱人的,但风险是使一个已经 robust 的系统 overcomplicating。一个独立的 display: masonry 模块提供了清晰度,但增加了 CSS 的学习曲线。Item Flow,最新的竞争者,提出了一个统一的系统,可以使砌体成为 Grid 和 Flexbox 的自然扩展,可能最终平息辩论。

每种方法都有权衡:

  • Grid with Masonry:熟悉但可能笨拙,有可访问性和规范问题。
  • 新模块:干净且 purpose-built,但需要学习新语法。
  • Item Flow:优雅且多功能,但尚不可用,有关于命名和实现的持续辩论。

Item Flow 增强现有布局同时支持砌体的能力使其成为一个引人注目的选项,但其成功取决于浏览器采用和社区支持。

结论

那么,在这一切之后,我们落在哪里?砌体对决归结为三条路径:将砌体扩展到 CSS Grid 中,砌体的独立模块,或 Item Flow。现在,问题是,CSS 最终会让我们从砌体的 JavaScript 中解放出来,还是我们仍在做梦?

Grid teasing us with a taste,而独立模块 whispering promises——但终点线 unclear,WebKit swoops in with a killer merge shorthand,Item Flow。浏览器 buy-in,社区 push,以及一些更多的规范修订可能会告诉我们。就目前而言,这是你的举动——测试、调整和权衡。答案正在来临,一次一个布局。

参考文献

  • “Native CSS Masonry Layout in CSS Grid” by Rachel Andrew
  • “Should Masonry be part of CSS Grid?” by Ahmad Shadeed
  • “CSS Masonry & CSS Grid” by Geoff Graham
  • “Masonry? In CSS?!” by Michelle Barker
  • “Native CSS Masonry Layout in CSS Grids” by Chris Coyier
  • “Item Flow Part 1: A Unified Concept for Layout” by WebKit

(gg, yk)

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计