序:Flex 解决了一半的问题

上一篇我们讲了 Flex 布局。Flex 很强大,但它本质上是一维布局——它要么控制行,要么控制列,无法同时精确管理两者。

当你需要做一个真正的二维网格:5 行 7 列,某些格子跨 2 列,某些格子跨 3 行,第 3 行整体偏移……Flex 就捉襟见肘了。

这就是 CSS Grid Layout 存在的意义。

Grid 是 CSS 历史上第一个真正的二维布局系统,2017 年被所有主流浏览器全面支持。如果说 Flex 是布局的瑞士军刀,那 Grid 就是精密铣床——它用一套声明式的语法,把整个页面变成一个你可以精确控制的网格。


一、Grid vs Flex:什么时候用哪个?

这是开发者最常问的问题。答案其实很简单:

对比维度 Flex(一维) Grid(二维)
维度 一次控制一个方向 同时控制行和列
设计思路 从内容出发,空间自适应 从容器出发,先画网格再放内容
适合场景 导航栏、按钮组、工具条 页面整体骨架、复杂网格、仪表盘
对齐控制 主轴/交叉轴对齐 行对齐 + 列对齐,独立控制
换行行为 flex-wrap 换行后行间关系断裂 网格轨道固定,项目跨格精确可控
学习曲线 平缓 稍陡,但概念清晰

一句话原则Flex 用于组件内部的微观布局,Grid 用于页面级别的宏观布局。 两者不是替代关系,而是互补。


二、核心概念:网格的六个术语

学 Grid 最大的门槛不是语法,是术语。搞懂这六个词,后面的一切都顺理成章。

网格六要素

术语 英文 说明
容器 Grid Container display: grid 激活的元素
项目 Grid Item 容器的直接子元素
行轨道 Row Track 网格的水平线之间的空间
列轨道 Column Track 网格的垂直线之间的空间
单元格 Cell 行和列交叉形成的最小单位
网格线 Grid Line 行和列的分界线,编号从 1 开始

一张图理解:

1
2
3
4
5
6
7
8
9
        列线1   列线2   列线3   列线4
│ │ │ │
行线1 ────┼───────┼───────┼───────┤
│ Cell │ Cell │ Cell │ ← 行轨道1
行线2 ────┼───────┼───────┼───────┤
│ Cell │ Cell │ Cell │ ← 行轨道2
行线3 ────┼───────┼───────┼───────┤
│ │ │ │
←─列轨道1─→←─列轨道2─→←─列轨道3─→

关键点:网格线编号从 1 开始,从左到右、从上到下递增。一个 3 列的网格有 4 条列线。后面的 grid-columngrid-row 属性就是用线号来定位项目的。


三、定义网格:容器属性详解

3.1 grid-template-columns / grid-template-rows:画格子

这是 Grid 最核心的属性——定义列和行的轨道尺寸。

1
2
3
4
5
.container {
display: grid;
grid-template-columns: 200px 1fr 1fr;
grid-template-rows: 80px 1fr 60px;
}

上面的代码画出了一个 3 列 3 行 的网格:第一列固定 200px,后两列等分剩余空间;第一行固定 80px,中间行自适应,最后一行 60px。

轨道尺寸单位一览

含义 示例
px / % 固定值或百分比 200px50%
fr 按比例分配剩余空间 1fr 2fr(1:2 分配)
auto 由内容决定大小 auto 1fr
minmax(min, max) 最小值和最大值之间 minmax(200px, 1fr)
repeat(n, value) 重复简化写法 repeat(3, 1fr) 等价于 1fr 1fr 1fr
fit-content(limit) 内容尺寸但不超限 fit-content(300px)

最实用的组合repeat(auto-fill, minmax(200px, 1fr)) —— 自动按 200px 最小宽度排列尽可能多的列,剩余空间均分。这是响应式卡片网格的最佳写法。

3.2 gap:网格间距

和 Flex 一样,Grid 也支持 gap,且语义更明确——可以分别设置行列间距。

1
2
3
4
5
6
.container {
gap: 16px; /* 行列间距都是 16px */
gap: 20px 12px; /* 行间距 20px,列间距 12px */
row-gap: 20px; /* 仅行间距 */
column-gap: 12px; /* 仅列间距 */
}

3.3 grid-template-areas:用名字画布局

这是 Grid 最直觉、最强大的特性。你可以用文字”画”出页面布局:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.container {
display: grid;
grid-template-columns: 240px 1fr 300px;
grid-template-rows: 80px 1fr 60px;
grid-template-areas:
"header header header"
"sidebar main aside"
"footer footer footer";
}

.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.aside { grid-area: aside; }
.footer { grid-area: footer; }

看到没?grid-template-areas 的每一行对应网格的一行,每个名字对应一个单元格。相同名字表示项目跨越多格。这种写法让布局意图一目了然,是 Grid 的灵魂特性。

规则

  • 同一名字必须形成矩形区域,不能出现 L 形或散点
  • . 表示空单元格
  • 区域名必须在项目上用 grid-area 引用

3.4 justify-items / align-items:单元格内对齐

控制每个项目在各自单元格内的对齐方式。

1
2
3
4
.container {
justify-items: start | end | center | stretch; /* 水平方向 */
align-items: start | end | center | stretch; /* 垂直方向 */
}
效果
stretch(默认) 拉伸填满单元格
start 靠单元格起点
end 靠单元格终点
center 单元格内居中

简写place-items: align-items justify-items,如 place-items: center center

3.5 justify-content / align-content:整个网格在容器内的对齐

当网格总尺寸小于容器时,控制整个网格的对齐。

1
2
3
4
.container {
justify-content: start | end | center | space-between | space-around | space-evenly;
align-content: start | end | center | space-between | space-around | space-evenly;
}

简写place-content: align-content justify-content

3.6 grid-auto-flow:自动排列方向

当项目没有显式指定位置时,Grid 按什么方向自动排列?

1
2
3
.container {
grid-auto-flow: row | column | row dense | column dense;
}
效果
row(默认) 按行排列,放满一行再换行
column 按列排列,放满一列再换列
dense 紧凑排列,尝试用前面的空位填充(可能打乱顺序)

dense 模式适合瀑布流/图片墙等不关心顺序的场景,能减少视觉空洞。但注意它可能改变项目的 DOM 顺序与视觉顺序的对应关系。


四、放置项目:项目属性详解

4.1 grid-column / grid-row:用线号定位

这是最精确的定位方式——用网格线编号指定项目的起止位置。

1
2
3
4
.item {
grid-column: 1 / 3; /* 从列线1到列线3,跨2列 */
grid-row: 2 / 4; /* 从行线2到行线4,跨2行 */
}

语法grid-column: start / endgrid-row: start / end

跨数简写:用 span 关键字表示跨越的轨道数。

1
2
3
4
.item {
grid-column: 1 / span 2; /* 从列线1开始,跨2列 */
grid-row: span 3; /* 自动定位,跨3行 */
}

4.2 grid-area:用名字定位

配合 grid-template-areas 使用,也可以作为 grid-row-start / grid-column-start / grid-row-end / grid-column-end 的简写。

1
2
3
4
5
6
7
8
/* 方式一:引用区域名 */
.header { grid-area: header; }

/* 方式二:线号简写 */
.item { grid-area: 2 / 1 / 4 / 3; }
/* 等价于:
grid-row-start: 2; grid-column-start: 1;
grid-row-end: 4; grid-column-end: 3; */

4.3 justify-self / align-self:单个项目对齐

覆盖容器的 justify-items / align-items,让单个项目”开小差”。

1
2
.container { justify-items: stretch; }
.special { justify-self: center; } /* 只有它居中 */

简写place-self: align-self justify-self

4.4 项目属性速查表

属性 作用 常用值
grid-column 列方向起止位置 1 / 31 / span 2
grid-row 行方向起止位置 2 / 4span 3
grid-area 区域名或线号简写 header2 / 1 / 4 / 3
justify-self 单个项目水平对齐 startendcenterstretch
align-self 单个项目垂直对齐 startendcenterstretch

五、实战案例(三个高频场景)

案例一:经典页面骨架

这是 Grid 最经典的用法——用 grid-template-areas 一眼看清页面结构。

1
2
3
4
5
6
7
<div class="page">
<header class="page-header">页头</header>
<nav class="page-nav">导航</nav>
<main class="page-main">主内容</main>
<aside class="page-aside">侧边栏</aside>
<footer class="page-footer">页脚</footer>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
.page {
display: grid;
min-height: 100vh;
grid-template-columns: 200px 1fr 260px;
grid-template-rows: 64px 1fr auto;
grid-template-areas:
"header header header"
"nav main aside"
"footer footer footer";
gap: 12px;
}

.page-header { grid-area: header; }
.page-nav { grid-area: nav; }
.page-main { grid-area: main; }
.page-aside { grid-area: aside; }
.page-footer { grid-area: footer; }

/* 响应式:窄屏时变为单列 */
@media (max-width: 768px) {
.page {
grid-template-columns: 1fr;
grid-template-areas:
"header"
"nav"
"main"
"aside"
"footer";
}
}

换行 grid-template-areas 就完成了移动端适配,不需要改 HTML 结构。这就是 Grid 的威力。

案例二:自动响应式卡片网格

1
2
3
4
5
6
7
8
9
10
11
12
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
gap: 20px;
}

.card {
padding: 20px;
border-radius: 8px;
background: #fff;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}

repeat(auto-fill, minmax(260px, 1fr)) 的魔法在于:

  • 屏幕宽时自动放更多列
  • 屏幕窄时自动减少列数
  • 每列最小 260px,剩余空间均分
  • 零媒体查询,纯 CSS 实现响应式

案例三:跨格仪表盘布局

1
2
3
4
5
6
7
8
9
10
11
12
.dashboard {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: auto repeat(2, 1fr) auto;
gap: 16px;
}

.widget-chart { grid-column: 1 / 3; grid-row: 2 / 4; } /* 大图表:跨2列2行 */
.widget-stats { grid-column: 3 / 5; grid-row: 2; } /* 统计条:跨2列 */
.widget-list { grid-column: 3; grid-row: 3; } /* 列表:1格 */
.widget-progress { grid-column: 4; grid-row: 3; } /* 进度:1格 */
.widget-footer { grid-column: 1 / 5; grid-row: 4; } /* 底部:跨满 */

用 Flex 实现这个布局需要大量嵌套和 hack,而 Grid 只需声明线号,精确且直观。


六、常见陷阱与最佳实践

6.1 陷阱一:fr 不是百分比

1
2
3
/* 以下两者行为不同 */
grid-template-columns: 1fr 1fr 1fr; /* 按剩余空间均分 */
grid-template-columns: 33.33% 33.33% 33.33%; /* 按容器总宽度算,gap会挤压 */

fr 分配的是扣除固定轨道和 gap 之后的剩余空间,所以不会因为 gap 导致溢出。推荐用 fr 而非 %

6.2 陷阱二:auto-fill vs auto-fit

1
2
repeat(auto-fill, minmax(200px, 1fr));  /* 创建空轨道占位 */
repeat(auto-fit, minmax(200px, 1fr)); /* 折叠空轨道,项目拉伸填满 */
模式 项目少时的表现
auto-fill 保留空列位置,项目不会拉伸
auto-fit 空列折叠,项目拉伸填满整行

经验:卡片网格用 auto-fill(保持卡片宽度一致),单行按钮组用 auto-fit(拉伸填满)。

6.3 陷阱三:隐式网格的尺寸

当项目超出 grid-template-rows/columns 定义的范围时,Grid 会自动创建隐式轨道。

1
2
3
4
5
6
.container {
grid-template-columns: repeat(3, 100px);
/* 如果有 10 个项目,第 4 行是隐式创建的 */
grid-auto-rows: 80px; /* 隐式行的高度 */
grid-auto-flow: row; /* 隐式排列方向 */
}

不加 grid-auto-rows,隐式行的高度由内容撑开,可能导致行高不一致。记得设置 grid-auto-rows

6.4 陷阱四:dense 模式的顺序问题

1
grid-auto-flow: row dense;

dense 会尝试用前面的空位填充后续项目,可能导致视觉顺序与 DOM 顺序不一致。这会影响:

  • 键盘 Tab 导航顺序
  • 屏幕阅读器朗读顺序

无障碍要求高的页面慎用 dense

6.5 最佳实践速查表

需求 代码
等分 N 列 grid-template-columns: repeat(N, 1fr)
响应式卡片 repeat(auto-fill, minmax(260px, 1fr))
固定侧边栏 + 自适应主区 grid-template-columns: 240px 1fr
跨列项目 grid-column: 1 / -1(跨满,-1 是最后一条线)
行列间距 gap: 16px
区域命名布局 grid-template-areas: "header header" "nav main"
单元格内居中 place-items: center
网格整体居中 place-content: center

七、Flex + Grid 协作:最佳搭档

实际项目中,Flex 和 Grid 经常配合使用。一个典型模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/* 外层用 Grid 做页面骨架 */
.page {
display: grid;
grid-template-areas:
"header header"
"nav main"
"footer footer";
grid-template-columns: 200px 1fr;
grid-template-rows: 60px 1fr 40px;
}

/* header 内部用 Flex 做导航栏 */
.page-header {
grid-area: header;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
}

/* main 内部用 Grid 做卡片网格 */
.page-main {
grid-area: main;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 16px;
padding: 20px;
}

原则:Grid 管宏观骨架,Flex 管微观排列。各司其职,代码清晰。


八、总结

Grid 的学习曲线比 Flex 陡一些,但它的表达能力是质变级的——用几行 CSS 就能描述出过去需要大量嵌套和 hack 才能实现的二维布局

记忆框架

  1. 容器调 display: grid
  2. 画格子用 grid-template-columns/rows
  3. 命名布局用 grid-template-areas
  4. 间距用 gap
  5. 项目定位用 grid-column/rowgrid-area
  6. 对齐用 place-items(单元格内)和 place-content(整体)

掌握这六步,你就拥有了 CSS 二维布局的终极武器。


延伸阅读


本文写于 2026 年 6 月 27 日,所有代码示例均基于最新 CSS Grid Level 1 规范,兼容 Chrome、Firefox、Safari、Edge 最新版本。