第24章 网格布局

第二十四章:Grid 布局

想象一下,如果你要布置一个房间,你会怎么做?买好家具之后,是不是要决定沙发放哪儿、电视放哪儿、茶几放哪儿?Grid 布局就是 CSS 给你的"房间布置图"——你可以精确地决定每一个元素该放在哪一行、哪一列。就像城市规划师一样,你现在是 CSS 布局的城市规划师!Flexbox 是单行道,只能往一个方向走;Grid 则是十字路口,行和列同时控制,这就是二维布局的威力!

24.1 基本概念

24.1.1 启用——display: grid(块级网格)或 display: inline-grid(行内网格)

Grid 布局的开启方式和 Flexbox 类似,只需要给父容器加上 display: griddisplay: inline-grid 即可。一旦开启,整个布局世界就变得规整起来——元素不再随心所欲地乱跑,而是乖乖地排列在你画好的网格里。

什么是 Grid 布局?

打个比方,Grid 就像是Excel表格——有行有列,每个格子都有固定的地址。你只需要告诉元素"你住第1行第2列",它就会乖乖搬过去。而 Flexbox 更像是俄罗斯方块——元素一个挨着一个排列,但没有固定的网格线。

 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
/* Grid 布局需要先在父容器上开启 */
.grid-container {
  /* display: grid 会让容器变成"网格容器" */
  /* 容器本身还是块级元素,会占据一整行 */
  display: grid;

  /* 设置列宽 */
  grid-template-columns: 200px 200px 200px;

  /* 设置行高 */
  grid-template-rows: 100px 100px 100px;

  /* 行列之间的间距 */
  gap: 20px;
}

/* display: inline-grid 则不同 */
.inline-grid-container {
  display: inline-grid;

  /* 它不会占据一整行 */
  /* 宽度由内容决定,像行内元素一样 */
  grid-template-columns: 150px 150px;
  gap: 10px;
}
1
2
3
4
5
6
7
8
9
<!-- 使用 Grid 布局的典型结构 -->
<div class="grid-container">
  <div class="grid-item">单元格1</div>
  <div class="grid-item">单元格2</div>
  <div class="grid-item">单元格3</div>
  <div class="grid-item">单元格4</div>
  <div class="grid-item">单元格5</div>
  <div class="grid-item">单元格6</div>
</div>

什么时候用 grid vs inline-grid

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
/* grid —— 块级网格,占据一整行 */
.full-width-grid {
  display: grid;
  /* 宽度默认100%,适合作为页面主布局 */
  grid-template-columns: repeat(3, 1fr);
}

/* inline-grid —— 行内网格,宽度由内容决定 */
.inline-width-grid {
  display: inline-grid;
  /* 适合放在文字中间的小网格 */
  grid-template-columns: 50px 50px 50px;
}

💡 小贴士display: grid 是最常用的开启方式,因为它会自动填满容器宽度,方便我们做响应式布局。

24.1.2 与 Flexbox 的区别——Flexbox 是一维布局(单行或单列),Grid 是二维布局(行和列同时控制)

这是一个经常被问到的问题:Grid 和 Flexbox 有什么区别?我应该用哪个?

用生活例子来解释

想象你要摆一排椅子(Flexbox)vs 布置一个教室(Grid):

  • Flexbox:你有一排椅子,只需要决定它们怎么排——靠左、靠右、居中、均匀分布。这一排椅子可以伸缩,但只有一排。
  • Grid:你有一个教室,要决定每排有几张桌子、每列有多宽、哪个学生坐哪个位置。行列同时控制。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
/* Flexbox 适合的场景:一维分布 */
.flex-nav {
  display: flex;
  /* 导航栏只需要水平排列,不需要关心"行"的概念 */
  justify-content: space-between;
  align-items: center;
  height: 60px;
}

/* Grid 适合的场景:二维分布 */
.grid-dashboard {
  display: grid;
  /* 仪表盘需要同时控制行和列 */
  grid-template-columns: 200px 1fr 200px;
  grid-template-rows: auto 1fr auto;
  height: 100vh;
}
Flexbox(Flexbox 一维布局):
┌─────────────────────────────────────┐
│  Logo    导航菜单        登录按钮  │
└─────────────────────────────────────┘
  → 只需要控制水平方向

Grid(Grid 二维布局):
┌─────────┬─────────────────┬─────────┐
│         │                 │         │
│  侧边栏  │      主内容      │  小工具  │
│         │                 │         │
│         ├─────────────────┤         │
│         │                 │         │
│         │                 │         │
└─────────┴─────────────────┴─────────┘
  → 同时控制行和列

什么时候用 Flexbox?什么时候用 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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/* 用 Flexbox:*/
/* 1. 导航栏、按钮组、水平居中 */
.navbar {
  display: flex;
  justify-content: space-between;
}

/* 2. 垂直居中 */
.center-wrapper {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100vh;
}

/* 3. 卡片列表(自动换行)*/
.card-list {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
}

/* 用 Grid:*/
/* 1. 页面整体布局 */
.page-layout {
  display: grid;
  grid-template-areas:
    "header header"
    "sidebar main"
    "footer footer";
  grid-template-rows: auto 1fr auto;
}

/* 2. 数据表格、相册网格 */
.photo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 16px;
}

/* 3. 需要精确控制元素位置 */
.exact-placement {
  display: grid;
  grid-template-columns: 1fr 2fr;
}

.featured-item {
  grid-column: 1 / 3;  /* 横跨整行 */
}

💡 小贴士:实际上,Flexbox 和 Grid 可以完美配合使用。用 Grid 做整体页面骨架,用 Flexbox 做组件内部布局,这是现代 CSS 布局的最佳实践!

24.2 容器属性

24.2.1 grid-template-columns——定义列宽

grid-template-columns 决定了你的网格有多少列、每列有多宽。这是 Grid 布局中最常用的属性之一。

基本语法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
/* 直接写死每列宽度 */
.fixed-columns {
  display: grid;
  grid-template-columns: 200px 300px 200px;
  /* 三列,分别是200px、300px、200px */
}

/* 使用 repeat() 函数简化 */
.repeated-columns {
  display: grid;
  grid-template-columns: repeat(3, 200px);
  /* 三列,每列200px,等于 200px 200px 200px */
}
1
2
3
4
5
<div class="fixed-columns">
  <div>第一列 200px</div>
  <div>第二列 300px</div>
  <div>第三列 200px</div>
</div>

fr 单位——Grid 专属的比例单位

fr 是"fraction(分数)“的缩写,它是 Grid 布局中最神奇的单位,代表"可用空间的等分”。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/* fr 单位的魔力 */
.equal-columns {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  /* 三列等宽,各占可用空间的1/3 */
  gap: 20px;
}

.ratio-columns {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;
  /* 三列,比例是1:2:1 */
  /* 如果容器宽度是1000px,则分别是250px、500px、250px */
  gap: 20px;
}
fr 单位计算示意(容器宽度1000px,间距20px × 3 = 60px,因为有4条隐式网格线产生3个间距):
可用空间 = 1000 - 60 = 940px

1fr 2fr 1fr 比例总和 = 1 + 2 + 1 = 4
第一列 = 940 × 1/4 = 235px
第二列 = 940 × 2/4 = 470px
第三列 = 940 × 1/4 = 235px

响应式神器:auto-fill 和 auto-fit

这两个关键字让 Grid 布局变得超级响应式,完全不需要媒体查询!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/* auto-fill:尽可能多地填充列 */
.auto-fill-grid {
  display: grid;
  /* 每列至少200px,自动计算能放几列 */
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 20px;
  /* 容器变宽就自动增加列数 */
}

/* auto-fit:和 auto-fill 类似,但空列会塌陷 */
.auto-fit-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 20px;
  /* 当内容撑不满时,auto-fit 会让列合并 */
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<!-- auto-fill vs auto-fit 的区别 -->
<div class="auto-fill-grid">
  <!-- auto-fill:保持列的位置,即使没有内容 -->
  <div>1</div>
  <div>2</div>
</div>
<!-- 如果只有2个元素,auto-fill 会留出第3列的位置 -->

<div class="auto-fit-grid">
  <div>1</div>
  <div>2</div>
</div>
<!-- 如果只有2个元素,auto-fit 会让它们自动扩展填满 -->

💡 小贴士:大多数情况下用 auto-fitauto-fill 更实用,因为它会自动适应内容数量。

24.2.2 grid-template-rows——定义行高

grid-template-rowsgrid-template-columns 类似,只不过控制的是行的高度。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/* 固定行高 */
.fixed-rows {
  display: grid;
  grid-template-rows: 100px 200px 100px;
  /* 三行,分别是100px、200px、100px */
}

/* 使用 minmax() 灵活设置 */
.flexible-rows {
  display: grid;
  grid-template-rows: minmax(100px, auto) 1fr minmax(80px, auto);
  /* 第一行最小100px,最大自适应 */
  /* 第二行占据剩余空间 */
  /* 第三行最小80px,最大自适应 */
}

24.2.3 grid-template-areas——用命名区域构建布局

这是 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
29
30
31
32
33
34
35
36
37
38
39
40
/* 用文字"画"出布局 */
.page-layout {
  display: grid;
  grid-template-areas:
    "header header header"    /* 第一行:header占满 */
    "sidebar main aside"      /* 第二行:sidebar、main、aside */
    "footer footer footer";   /* 第三行:footer占满 */
  grid-template-rows: 60px 1fr 50px;  /* 行高分别是60px、1fr、50px */
  grid-template-columns: 200px 1fr 200px;  /* 列宽分别是200px、1fr、200px */
  min-height: 100vh;
  gap: 1px;
  background-color: #eee;  /* gap的颜色 */
}

/* 告诉每个元素它属于哪个区域 */
.page-header {
  grid-area: header;    /* 对应 header */
  background: #3498db;
}

.page-sidebar {
  grid-area: sidebar;  /* 对应 sidebar */
  background: #2ecc71;
}

.page-main {
  grid-area: main;      /* 对应 main */
  background: white;
}

.page-aside {
  grid-area: aside;     /* 对应 aside */
  background: #f39c12;
}

.page-footer {
  grid-area: footer;    /* 对应 footer */
  background: #34495e;
  color: white;
}
1
2
3
4
5
6
7
<div class="page-layout">
  <header class="page-header">我是头部,会横跨整行</header>
  <aside class="page-sidebar">我是侧边栏</aside>
  <main class="page-main">我是主内容区</main>
  <aside class="page-aside">我是右边栏</aside>
  <footer class="page-footer">我是底部,会横跨整行</footer>
</div>

grid-template-areas 可视化

你写的代码:
grid-template-areas:
  "header header header"
  "sidebar main aside"
  "footer footer footer";

实际效果:
┌─────────┬─────────┬─────────┐
│           header              │
├─────────┼─────────┼─────────┤
│  sidebar │   main  │  aside  │
├─────────┼─────────┼─────────┤
│           footer              │
└─────────┴─────────┴─────────┘

24.2.4 grid-auto-columns / rows——控制隐式网格尺寸

当你的 HTML 内容比 grid-template 定义的多时,Grid 会自动创建"隐式"行或列。grid-auto-columnsgrid-auto-rows 就是用来控制这些自动创建的行列尺寸的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* 场景:你的内容可能有10个,但只定义了3个 */
.auto-columns-grid {
  display: grid;
  /* 只定义了3列,每列1fr */
  grid-template-columns: repeat(3, 1fr);
  /* 但内容可能有20个div! */

  /* 隐式列的宽度控制 */
  grid-auto-columns: 150px;
  /* 超出3列的内容,每列宽度150px */
}

/* 隐式行的高度控制 */
.auto-rows-grid {
  display: grid;
  grid-template-rows: 100px 100px;
  /* 只定义2行 */

  /* 隐式行的高度控制 */
  grid-auto-rows: 80px;
  /* 超出2行的内容,每行高度80px */
}

24.2.5 grid-auto-flow——row(按行排列,默认)/ column(按列排列)/ dense(自动填充空位)

grid-auto-flow 控制元素如何流入网格。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* 默认:按行排列 */
.row-flow {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-flow: row;  /* 默认值 */
  /* 元素填满一行再去下一行 */
}

/* 按列排列 */
.column-flow {
  display: grid;
  grid-template-columns: repeat(2, 1fr);  /* 先定义2列 */
  grid-auto-flow: column;                 /* 然后告诉 Grid 按列填充 */
  /* 元素填满第1列的3个格子,再去第2列的3个格子 */
}

/* 开启自动填充(自动填满空位)*/
.dense-flow {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-flow: dense;
  /* 如果有元素位置空出来,会自动尝试用后面的元素填充 */
}

24.2.6 grid-template 缩写——grid-template: rows / columns

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/* 完整写法 */
.full-grid {
  display: grid;
  grid-template-columns: 200px 1fr 200px;
  grid-template-rows: auto 1fr auto;
  gap: 20px;
}

/* 缩写写法 */
.shorthand-grid {
  display: grid;
  /* rows / columns */
  grid-template:
    auto 1fr auto / 200px 1fr 200px;
  gap: 20px;
}

24.3 单位与函数

24.3.1 fr 单位——相对于可用空间的分数

fr 是 Grid 布局的灵魂单位,它代表"可用空间的等分"。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/* fr 的工作原理 */
.fr-magic {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;
  /* 假设容器宽度是 1000px,gap 是 40px(20px × 2)*/
  gap: 20px;
  /* 可用空间 = 1000 - 40 = 960px */
  /* 比例总和 = 1 + 2 + 1 = 4 */
  /* 第一列 = 960 × 1/4 = 240px */
  /* 第二列 = 960 × 2/4 = 480px */
  /* 第三列 = 960 × 1/4 = 240px */
}

fr 和固定单位混用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/* 混用 fr 和固定单位 */
.mixed-columns {
  display: grid;
  grid-template-columns: 250px 1fr 1fr;
  /* 左侧边栏固定250px,右侧两列平分剩余空间 */
  /* 如果容器1200px,剩余空间 = 1200 - 250 - 40(gap) = 910px */
  /* 第二列 = 910 × 1/2 = 455px */
  /* 第三列 = 910 × 1/2 = 455px */
  /* 注意:这里的 1fr 不是 250px,而是平分 910px 后的 455px! */
}

24.3.2 minmax(min, max)——定义尺寸范围

minmax() 函数让你可以设置一个"最小到最大"的范围。翻译成人话就是:“别小于 X,别大于 Y,自己看着办!”

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/* minmax(最小值, 最大值) */
.minmax-grid {
  display: grid;
  grid-template-columns: minmax(200px, 1fr) 1fr;
  /* 第一列:最小200px,最大能有多宽就多宽(1fr)*/
  /* 第二列:平分剩余空间 */
}

/* 常用组合:minmax(auto, 300px) */
.self-adjust {
  display: grid;
  grid-template-columns: minmax(auto, 300px) 1fr;
  /* 第一列:由内容决定宽度,但最多300px(内容少就窄,内容多也不超过300px)*/
  /* 适合那种"窄了不行、宽了也没用"的场景 */
}

实际应用:响应式列

1
2
3
4
5
6
7
8
/* 最经典的响应式 Grid */
.responsive-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 20px;
  /* 每列最小250px,自动计算能放几列 */
  /* 容器变窄就自动换行,不需要媒体查询 */
}

24.3.3 auto-fill 和 auto-fit——自动填充

这两个是 Grid 响应式的神器,但它们有微妙的区别。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
/* auto-fill:尽可能多地填充列,保留空列位置 */
.auto-fill-demo {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 20px;
}

/* auto-fit:自动扩展填满可用空间 */
.auto-fit-demo {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 20px;
}
auto-fill vs auto-fit 对比(容器宽度800px,3个元素,每个minmax 150px):

auto-fill(保留空列):
┌────────┬────────┬────────┬────────┐
│  元素1  │  元素2  │  元素3  │  (空列) │
└────────┴────────┴────────┴────────┘

auto-fit(自动扩展):
┌─────────────────┬─────────────────┐
│     元素1        │     元素2        │
├─────────────────┴─────────────────┤
│              元素3                   │
└───────────────────────────────────┘

24.3.4 min-content / max-content——内容的最小/最大尺寸

1
2
3
4
5
6
7
8
/* 根据内容自适应 */
.content-grid {
  display: grid;
  grid-template-columns: min-content max-content 1fr;
  /* 第一列宽度由最长内容决定 */
  /* 第二列宽度由内容决定 */
  /* 第三列平分剩余 */
}

24.4 项目定位

24.4.1 grid-column-start / end——列线定位

Grid 有"网格线",每条线都有编号(1、2、3…)。

1
2
3
4
5
/* 定位到特定网格线 */
.special-item {
  grid-column-start: 1;  /* 从第1条线开始 */
  grid-column-end: 3;      /* 到第3条线结束(跨2列)*/
}
网格线编号示意(3列4行):
     1      2      3      4
   ┌──────┬──────┬──────┐
1  │      │      │      │
   ├──────┼──────┼──────┤
2  │      │ 跨2列│      │
   │      │ (1→3)│      │
   └──────┴──────┴──────┘

24.4.2 grid-row-start / end——行线定位

1
2
3
4
5
/* 跨2行 */
.vertical-span {
  grid-row-start: 1;
  grid-row-end: 3;  /* 从第1行到第3行(跨2行)*/
}

24.4.3 grid-column / grid-row 缩写

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/* 缩写语法 */
.shorthand-item {
  /* grid-column: 开始线 / 结束线; */
  grid-column: 1 / 3;  /* 从第1线到第3线 */
  grid-row: 1 / 2;    /* 从第1行到第2行 */
}

/* 使用 span 关键字 */
.span-item {
  grid-column: 1 / span 2;  /* 从第1线开始,跨2列 */
  grid-row: span 3;         /* 跨3行 */
}

24.4.4 span 关键字——跨越列/行

1
2
3
4
5
6
7
8
/* span 关键字让计算更简单 */
.span-examples {
  /* 从当前列开始,跨2列 */
  grid-column: span 2;

  /* 从第2行开始,跨3行 */
  grid-row: 2 / span 3;
}

24.4.5 grid-area——指定项目对应的命名区域

1
2
3
4
/* 对应 grid-template-areas 中的命名 */
.feature-article {
  grid-area: featured;  /* 对应 .featured 区域 */
}

24.4.6 命名网格线

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
/* 给网格线起名字 */
.named-lines {
  display: grid;
  grid-template-columns:
    [col-start] 200px
    [col-content] 1fr
    [col-end];
  gap: 20px;
}

.named-item {
  grid-column: col-start / col-content;
}

24.5 对齐属性

24.5.1 justify-items——网格项目在单元格内的水平对齐

1
2
3
4
5
/* 项目在各自单元格内的水平对齐 */
.justify-start { justify-items: start; }
.justify-center { justify-items: center; }
.justify-end { justify-items: end; }
.justify-stretch { justify-items: stretch; }  /* 默认,拉伸填满 */

24.5.2 align-items——网格项目在单元格内的垂直对齐

1
2
3
4
5
/* 项目在各自单元格内的垂直对齐 */
.align-start { align-items: start; }
.align-center { align-items: center; }
.align-end { align-items: end; }
.align-stretch { align-items: stretch; }  /* 默认 */

24.5.3 place-items——align-items 和 justify-items 的缩写

1
2
3
4
5
/* place-items 是 align-items 和 justify-items 的缩写 */
/* 语法:place-items: <align-items> <justify-items>; */

.both-center { place-items: center; }           /* 水平和垂直都居中 */
.aligned-start { place-items: start end; }      /* 水平start,垂直end */

24.5.4 justify-content——整个网格在容器内的水平对齐

当网格总尺寸小于容器时,可以控制网格在容器内的对齐方式。

1
2
3
4
5
/* 网格在容器内的水平对齐 */
.justify-content-start { justify-content: start; }
.justify-content-center { justify-content: center; }
.justify-content-end { justify-content: end; }
.justify-content-space-between { justify-content: space-between; }

24.5.5 align-content——整个网格在容器内的垂直对齐

1
2
3
/* 网格在容器内的垂直对齐 */
.align-content-center { align-content: center; }
.align-content-space-between { align-content: space-between; }

24.5.6 place-content——align-content 和 justify-content 的缩写

1
2
/* place-content 是 align-content 和 justify-content 的缩写 */
.center-grid { place-content: center; }

24.5.7 justify-self / align-self——单个项目覆盖对齐方式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/* 单独控制某个项目的对齐 */
.self-item {
  justify-self: center;  /* 水平居中 */
  align-self: end;        /* 垂直底部对齐 */
}

/* 或者用缩写 */
.self-item {
  place-self: center end;
}

24.5.8 place-self——align-self 和 justify-self 的缩写

1
2
3
.self-centered {
  place-self: center;  /* 水平和垂直都居中 */
}

24.6 Grid 固定搭配

24.6.1 响应式卡片网格

这是最经典的 Grid 应用,完全不需要媒体查询!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
/* 响应式卡片网格 */
.responsive-card-grid {
  display: grid;
  /* 每张卡片最小250px,自动计算能放几列 */
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 24px;
  padding: 24px;
}

.card {
  background: white;
  border-radius: 12px;
  padding: 24px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  transition: transform 0.2s, box-shadow 0.2s;
}

.card:hover {
  transform: translateY(-4px);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<div class="responsive-card-grid">
  <div class="card">
    <h3>卡片标题</h3>
    <p>卡片内容卡片内容卡片内容卡片内容</p>
  </div>
  <div class="card">
    <h3>另一张卡片</h3>
    <p>响应式布局让每张卡片自动适应空间</p>
  </div>
  <div class="card">
    <h3>第三张卡片</h3>
    <p>不需要任何媒体查询!</p>
  </div>
</div>

24.6.2 圣杯布局

经典的三栏布局,用 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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/* 圣杯布局 */
.holy-grail {
  display: grid;
  grid-template-areas:
    "header header header"
    "sidebar main aside"
    "footer footer footer";
  grid-template-rows: 70px 1fr 60px;
  grid-template-columns: 220px 1fr 180px;
  min-height: 100vh;
  gap: 0;
}

.holy-grail > header {
  grid-area: header;
  background: #3498db;
  color: white;
  display: flex;
  align-items: center;
  padding: 0 24px;
}

.holy-grail > aside.sidebar {
  grid-area: sidebar;
  background: #ecf0f1;
  padding: 24px;
}

.holy-grail > main {
  grid-area: main;
  padding: 24px;
  background: white;
}

.holy-grail > aside.aside {
  grid-area: aside;
  background: #f8f9fa;
  padding: 24px;
}

.holy-grail > footer {
  grid-area: footer;
  background: #2c3e50;
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
}

24.6.3 12 栏系统

设计系统常用的12栏网格。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* 12栏网格系统 */
.col-12 {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 20px;
}

/* 各占几列的辅助类 */
.col-span-1 { grid-column: span 1; }
.col-span-2 { grid-column: span 2; }
.col-span-3 { grid-column: span 3; }
.col-span-4 { grid-column: span 4; }
.col-span-6 { grid-column: span 6; }
.col-span-8 { grid-column: span 8; }
.col-span-12 { grid-column: span 12; }

/* 响应式版本 */
@media (max-width: 768px) {
  [class*="col-span-"] {
    grid-column: span 12;
  }
}

24.7 subgrid 子网格

24.7.1 基本用法——grid-template-columns: subgrid 或 grid-template-rows: subgrid

Subgrid 是 Grid 布局中相对较新的功能,让嵌套的网格可以"继承"父网格的轨道。

⚠️ 注意:下面这个例子先演示一个常见的错误观念——很多人以为给子元素开 display: grid 再用 span 就能跨父轨道。错!那只是子元素自己建了一个等宽列网格,和父网格半毛钱关系都没有!真正的 subgrid 要用 subgrid 关键字。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/* ❌ 错误示范:这不是 subgrid! */
.card-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
}

.card {
  display: grid;
  grid-row: span 3;              /* 跨父网格的3行 ✓ */
  grid-template-rows: auto 1fr auto; /* 但这个是子网格自己定义的行,不是继承! */
}

.card-image {
  grid-column: span 3;  /* 这是卡片的3列,不是父网格的列! */
}

24.7.2 典型场景——对齐卡片网格内部内容

下面才是真正的 subgrid——用 grid-template-columns: subgrid 让卡片的列轨道直接继承父网格:

 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
/* ✅ 正确示范:用 subgrid 让卡片继承父网格的列轨道 */
.product-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 32px;
}

.product-card {
  display: grid;
  /* 子网格继承父网格的列轨道 */
  grid-template-columns: subgrid;
  grid-row: span 3;  /* 跨父网格的3行 */
}

.product-card img {
  grid-column: 1 / -1;  /* 横跨父网格的所有列 ✓ */
}

.product-card h3 {
  grid-column: 1 / -1;
}

.product-card p {
  grid-column: 1 / -1;
}

.product-card button {
  grid-column: 1 / -1;
}
 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
/* 让所有卡片的标题对齐、内容对齐、按钮对齐 */
.product-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 32px;
}

.product-card {
  display: grid;
  /* 子网格继承父网格的列轨道 */
  grid-template-columns: subgrid;
  grid-row: span 3;
}

.product-card img {
  grid-column: 1 / -1;  /* 横跨所有列 */
}

.product-card h3 {
  grid-column: 1 / -1;
}

.product-card p {
  grid-column: 1 / -1;
}

.product-card button {
  grid-column: 1 / -1;
}

24.8 Grid 常见坑

24.8.1 隐式网格行高不受控——当项目数量超过显式定义的行数时生效

😱 恐怖故事:你定义了一个 grid-template-rows: 100px 100px,自信满满地以为只会有 2 行。结果 HTML 里莫名其妙多了 8 个元素,Grid 默默给你创建了 8 行隐式网格,每行高度由内容决定——你的完美布局瞬间崩塌。解决方案?grid-auto-rows: minmax(100px, auto),让 Grid 知道隐式行也得守规矩!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/* 问题:内容超出时行高不受控制 */
.problem-grid {
  display: grid;
  grid-template-rows: 100px 100px;
  /* 只有2行,但可能有10个元素 */
}

/* 解决方案:设置隐式行高 */
.solution-grid {
  display: grid;
  grid-template-rows: 100px 100px;
  /* 控制自动创建的行的尺寸 */
  grid-auto-rows: minmax(100px, auto);
}

24.8.2 gap vs grid-gap——grid-gap 是旧名,gap 是标准写法

💀 考古时间:你可能会在一些老代码里看到 grid-gapgrid-row-gapgrid-column-gap。这些属性已经「入土为安」了——CSS 标准把它们合并成了 gaprow-gapcolumn-gap。所以,看到 grid-gap 就当它是个遗留文物,别再用了!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/* 旧写法(已废弃)*/
.old-grid {
  grid-gap: 20px;
  /* 或者 */
  grid-gap: 20px 10px;  /* 行间距 列间距 */
}

/* 标准写法 */
.new-grid {
  gap: 20px;
  /* 或者分别设置 */
  row-gap: 20px;
  column-gap: 10px;
}

24.8.3 fr 单位在有固定大小时的行为

1
2
3
4
5
6
7
8
9
/* fr 计算的是"可用空间" */
.calculation-grid {
  display: grid;
  grid-template-columns: 200px 1fr 200px;
  width: 1000px;
  gap: 20px;
  /* 可用空间 = 1000 - 200 - 200 - 20 - 20 = 560px */
  /* 1fr = 560px */
}

本章小结

恭喜你完成了第二十四章的学习!Grid 布局是 CSS 最强大的布局系统之一。

核心知识点

属性说明
display: grid启用网格布局
grid-template-columns定义列宽
grid-template-rows定义行高
grid-template-areas用文字描述布局
fr 单位可用空间的等分
minmax()尺寸范围
auto-fit / auto-fill自动填充列
grid-column / row项目定位
justify-items / align-items单元格内对齐
place-items对齐缩写

Grid vs Flexbox

graph TD
    A["布局选择"] --> B["Flexbox 一维"]
    A --> C["Grid 二维"]

    B --> B1["单行/单列排列"]
    B1 --> B2["导航栏"]
    B1 --> B3["按钮组"]
    B1 --> B4["水平/垂直居中"]

    C --> C1["行和列同时控制"]
    C1 --> C2["页面整体布局"]
    C1 --> C3["相册网格"]
    C1 --> C4["数据表格"]

    style A fill:#f39c12,stroke:#333,stroke-width:3px
    style C fill:#2ecc71,stroke:#333

实战建议

  1. 页面布局用 Grid:整体骨架用 Grid 更规范
  2. 组件内部用 Flexbox:卡片、导航等内部布局用 Flexbox
  3. 响应式用 auto-fitrepeat(auto-fit, minmax(250px, 1fr)) 是黄金组合
  4. 用命名区域grid-template-areas 让布局代码可读性更高

下章预告

下一章我们将学习响应式设计,让网页适配各种屏幕尺寸!