ToolPal
开发者在多个显示器上处理代码

CSS Grid:无需猜测构建复杂布局

📷 Christina Morillo / Pexels

CSS Grid:无需猜测构建复杂布局

grid-template-columns、fr单位、auto-fill与auto-fit的区别 — 从头构建照片画廊和仪表板等真实布局。附带可视化生成器。

D作者: Daniel Park2026年3月31日3分钟阅读

为什么我花了这么长时间才真正学会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-fillauto-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-fillauto-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-rowsgrid-auto-columns)也值得了解。当项目被自动放置到未明确定义的行中时,grid-auto-rows设置它们的高度:

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 200px; /* 所有自动放置的行高200px */
}

如果您希望自动放置的项目填充列而不是行,grid-auto-flow: column会翻转放置方向。


让我豁然开朗的时刻

Grid不再让我感到害怕的时刻,是当我停止试图记忆每个属性,只是内化了三件事:

  1. 您在容器上定义列和行,而不是在项目上。
  2. fr单位按比例分配空闲空间。
  3. repeat(auto-fill, minmax(min, 1fr))是响应式网格的模式。

其他一切——命名区域、跨度、明确放置——您可以随时查找。但这三个概念让您通过80%的现实Grid使用。

CSS Grid是那种一旦理解就感觉在为您工作的工具之一。2015年需要50行浮动或深度嵌套的flexbox才能完成的布局,现在用10行Grid就可以完成。这是真正的生活质量改善,一段时间后您会很难记得它之前的布局是什么感觉。

常见问题

D

关于作者

Daniel Park

Senior frontend engineer based in Seoul. Seven years of experience building web applications at Korean SaaS companies, with a focus on developer tooling, web performance, and privacy-first architecture. Open-source contributor to the JavaScript ecosystem and founder of ToolPal.

了解更多

分享文章

XLinkedIn

相关文章