
CSS Grid:无需猜测构建复杂布局
📷 Christina Morillo / PexelsCSS Grid:无需猜测构建复杂布局
grid-template-columns、fr单位、auto-fill与auto-fit的区别 — 从头构建照片画廊和仪表板等真实布局。附带可视化生成器。
为什么我花了这么长时间才真正学会Grid
我避开CSS Grid的时间比应该的要长。Flexbox满足了我的大多数需求,每次我浏览Grid文档时都会遇到"隐式轨道"和"命名网格线"之类的术语,然后悄悄关闭标签页。感觉是过度设计。
改变我想法的是尝试用Flexbox构建仪表板布局。我有侧边栏、标题、主内容区域和页脚,为了让它们对齐,我将flex容器嵌套了三层。勉强工作——但每当设计改变,我就不得不重构HTML。那时有人向我指出了Grid,大约一小时内我用30行CSS重写了整个布局。HTML几乎没有变化。
这就是Grid能做而Flexbox不能做的事:它让您从容器定义二维布局,而不必关心项目嵌套。您定义结构,项目自然融入其中。
本指南涵盖您日常使用Grid实际需要的一切——核心属性、奇特但有用的函数,以及我在真实项目中使用过的模式。如果您想在阅读时进行可视化实验,ToolBox Hubs上的CSS Grid生成器让您实时配置列、行和间距,并查看生成的CSS。
心智模型
在任何代码之前,先了解心智模型:CSS Grid要求您像平面设计师思考布局网格一样思考布局。您定义一系列水平线和垂直线,这些线创建单元格。然后您将内容放入这些单元格,或者让浏览器自动放置。
这与Flexbox有本质不同,Flexbox的布局由项目本身驱动——您在项目上设置属性,告诉它们如何增长、缩小或对齐。Grid颠倒了这一点。布局在容器上定义,项目遵循它。
定义网格
从一个属性开始:
.container {
display: grid;
}
这本身做不了多少。项目默认会堆叠在单列中。有趣的部分是定义轨道。
grid-template-columns和grid-template-rows
这些定义列和行的大小。值是以空格分隔的轨道大小列表。
.container {
display: grid;
grid-template-columns: 200px 400px 200px;
grid-template-rows: 100px auto 60px;
}
这创建三列(200px、400px、200px)和三行(100px、内容需要的高度、60px)。项目从左到右、从上到下放置在单元格中。
您可以使用任何CSS长度单位:px、em、rem、%、vw。但Grid引入的最有用的单位是全新的。
fr单位
fr单位代表分数。它表示网格容器中可用空闲空间的一份。以下是它比百分比好得多的原因:
/* 百分比方式——您必须做数学 */
.container {
display: grid;
grid-template-columns: 25% 50% 25%;
}
/* fr方式——您只需描述比例 */
.container {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
}
两者创建相同的布局,但fr正确处理间距。如果您在列之间添加gap并使用百分比,总宽度超过100%会产生溢出。使用fr时,浏览器计算减去间距后的空闲空间,然后分配它。减少了很多数学运算。
.container {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
gap: 24px;
/* 浏览器先减去两个24px间距,
然后以1:2:1的比例分割剩余部分 */
}
您还可以将固定宽度与fr单位混合,这是大多数真实布局的工作方式:
.layout {
display: grid;
grid-template-columns: 280px 1fr;
/* 侧边栏始终280px,内容占其余所有空间 */
}
那个模式——固定侧边栏,灵活内容——是我经常使用的。
repeat()函数
当您有许多等宽列时,手动写出它们很繁琐。repeat()是简写:
/* 不用repeat */
.grid {
grid-template-columns: 1fr 1fr 1fr 1fr;
}
/* 用repeat */
.grid {
grid-template-columns: repeat(4, 1fr);
}
repeat(4, 1fr)意味着"四条轨道,每条1fr宽"。您也可以重复复杂的模式:
.grid {
grid-template-columns: repeat(3, 100px 1fr);
/* 创建:100px 1fr 100px 1fr 100px 1fr */
}
但真正强大的版本是将repeat()与两个特殊关键字结合。
auto-fill和auto-fit
这是Grid从"有用"变成"有点神奇"的地方。与其指定确切的列数,不如告诉浏览器创建尽可能多的列:
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, 200px);
gap: 16px;
}
这创建了一个根据容器宽度自动调整列数的网格。在宽屏上可能得到5列,在窄屏上得到2列。不需要媒体查询。
auto-fill和auto-fit的区别微妙但有意义:
auto-fill创建尽可能多的轨道,包括空的。如果您只有3个项目但网格可以容纳6列,则获得6列——最后3个为空但占据空间。
auto-fit也创建尽可能多的轨道,但将空轨道折叠为零宽度。如果您在可以容纳6列的网格中有3个项目,空轨道折叠,您的3个项目伸展以填充可用空间。
/* auto-fill:空列保留空间 */
.fill-grid {
grid-template-columns: repeat(auto-fill, 200px);
}
/* auto-fit:项目扩展以填充容器 */
.fit-grid {
grid-template-columns: repeat(auto-fit, 200px);
}
选择哪个取决于设计。对于希望项目保持一致大小的照片画廊,auto-fill通常更好。对于希望项目填满行的卡片网格,auto-fit是正确的选择。
minmax()
拼图的最后一块是minmax(),它为轨道定义大小范围而不是固定大小。它接受两个参数:最小大小和最大大小。
.grid {
display: grid;
grid-template-columns: repeat(3, minmax(200px, 1fr));
}
这创建三列,每列至少200px宽,最多1fr的可用空间。在宽屏上它们扩展。在窄屏上它们永远不会小于200px。
repeat()、auto-fill或auto-fit、以及minmax()的组合非常常见,已经成为规范的响应式网格模式:
.responsive-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 24px;
}
读作:"创建尽可能多的列,每列至少240px宽,每列均等增长以填充可用空间。"项目自动换行。没有媒体查询。没有JavaScript。这是我需要快速创建响应式卡片网格时使用的模式。
真实示例:照片画廊
让我从头构建一个照片画廊来展示这是如何运作的。
<div class="gallery">
<img src="photo-1.jpg" alt="...">
<img src="photo-2.jpg" alt="...">
<!-- more photos -->
</div>
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 8px;
}
.gallery img {
width: 100%;
aspect-ratio: 1 / 1;
object-fit: cover;
display: block;
}
就这些。网格自动计算容器中能容纳多少列。图片使用aspect-ratio保持正方形比例,object-fit: cover处理裁剪。在手机上可能得到2列,在宽桌面上可能得到6列。布局本质上是响应式的,而不是通过断点列表。
如果您想突出显示某些照片,可以让它们跨越多个单元格:
.gallery img:first-child {
grid-column: span 2;
grid-row: span 2;
}
这使第一张照片占据2x2块,而其他所有照片围绕它流动。没有定位技巧,没有浮动——只是Grid放置。
真实示例:仪表板布局
这是让我接受Grid的用例。一个有侧边栏、标题、主内容和页脚的仪表板。
<div class="dashboard">
<header class="dash-header">Header</header>
<nav class="dash-sidebar">Sidebar</nav>
<main class="dash-main">Main Content</main>
<footer class="dash-footer">Footer</footer>
</div>
.dashboard {
display: grid;
grid-template-columns: 260px 1fr;
grid-template-rows: 60px 1fr 40px;
grid-template-areas:
'header header'
'sidebar main'
'footer footer';
min-height: 100vh;
gap: 0;
}
.dash-header { grid-area: header; }
.dash-sidebar { grid-area: sidebar; }
.dash-main { grid-area: main; }
.dash-footer { grid-area: footer; }
grid-template-areas属性使这特别易读。您基本上是在用ASCII艺术绘制布局。标题和页脚跨越两列。侧边栏是260px。主内容填满其余所有空间。
使其响应式很简单:
@media (max-width: 768px) {
.dashboard {
grid-template-columns: 1fr;
grid-template-rows: 60px auto 1fr 40px;
grid-template-areas:
'header'
'sidebar'
'main'
'footer';
}
}
在手机上,所有东西堆叠在单列中。侧边栏折叠到标题下方。整洁。
Grid与Flexbox:真正的答案
人们把这当作必须选边站的问题。您不必选。它们解决不同的问题。
使用Grid的情况:
- 您需要行和列相互对齐
- 您在构建页面级结构(标题、侧边栏、主体、页脚)
- 您需要项目跨越多行或多列
- 您想从容器而不是项目定义布局
- 您在构建每张卡片内有对齐内容区域的一致卡片网格
使用Flexbox的情况:
- 您有单行或单列的项目
- 项目需要根据其内容调整大小
- 您在内联排列导航链接、按钮或表单输入
- 您需要项目换行但不关心跨行对齐
在大多数项目中我两者都用。Grid处理页面骨架。Flexbox处理组件内部——导航栏、卡片内的按钮组、表单布局。它们是互补的,不是竞争的。
我听到的最清晰的规则是:"Grid用于布局。Flexbox用于对齐。"这是简化,但大多数时候能引导您做出正确选择。
使用CSS Grid生成器
如果您在构建网格布局,想在将其提交到代码之前看到结果,值得打开ToolBox Hubs上的CSS Grid生成器。您可以设置列和行数、调整间距、可视化配置列和行大小,并获得精确的CSS输出粘贴到您的项目中。
在处理grid-template-areas时特别有用——使用可视化工具比盯着代码更容易可视化哪些区域跨越哪些单元格。您配置一次布局,复制CSS,然后放入。比手动编写更快,是在构建之前快速测试布局想法的好方法。
浏览器支持和实际注意事项
Grid有强大的浏览器支持——所有现代浏览器自2017年以来都完全支持。您不需要供应商前缀。您不需要polyfill。写好发布即可。
一个真正的注意事项:Internet Explorer 11。IE 11有一个使用基本上与现代Grid语法不兼容的-ms-前缀属性的旧Grid实现。如果您的分析显示您关心的IE 11流量,Grid的使用需要仔细考虑或备用方案。
对于不支持IE 11的人——说实话,现在大多数人都不支持——Grid无需任何注意事项即可投入生产。
需要注意的一个属性:subgrid。它让子网格继承并参与父网格的轨道定义,这对于对齐嵌套内容很有用。Safari在2023年添加了支持,Chrome在2023年底。截至2026年初,各浏览器的支持已经稳定,但如果您在有旧浏览器要求的项目上构建,请验证您的具体目标。
您实际会使用的属性
在多个项目中使用Grid之后,这些是我最常用的属性:
/* 核心定义 */
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
grid-template-rows: auto;
/* 复杂布局的命名区域 */
grid-template-areas: 'header' 'sidebar main' 'footer';
/* 放置项目 */
grid-column: span 2;
grid-row: span 2;
/* 或明确放置 */
grid-column: 1 / 3; /* 从第1行开始,在第3行结束 */
grid-row: 2 / 4;
/* 间距 */
gap: 24px;
column-gap: 24px;
row-gap: 16px;
隐式网格属性(grid-auto-rows、grid-auto-columns)也值得了解。当项目被自动放置到未明确定义的行中时,grid-auto-rows设置它们的高度:
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 200px; /* 所有自动放置的行高200px */
}
如果您希望自动放置的项目填充列而不是行,grid-auto-flow: column会翻转放置方向。
让我豁然开朗的时刻
Grid不再让我感到害怕的时刻,是当我停止试图记忆每个属性,只是内化了三件事:
- 您在容器上定义列和行,而不是在项目上。
fr单位按比例分配空闲空间。repeat(auto-fill, minmax(min, 1fr))是响应式网格的模式。
其他一切——命名区域、跨度、明确放置——您可以随时查找。但这三个概念让您通过80%的现实Grid使用。
CSS Grid是那种一旦理解就感觉在为您工作的工具之一。2015年需要50行浮动或深度嵌套的flexbox才能完成的布局,现在用10行Grid就可以完成。这是真正的生活质量改善,一段时间后您会很难记得它之前的布局是什么感觉。