CSS Grid 网格布局
这一课你将能做到
- 使用 CSS Grid 构建二维网格布局,定义行、列和间距
- 使用
grid-template-areas 以可视化方式命名和放置页面区域
- 结合
minmax()、auto-fill、auto-fit 实现真正的响应式卡片网格,无需媒体查询
为什么要学
Flexbox 解决了一维布局(一行或一列),但现代网页本质上是二维的:整体页面有头部、侧边栏、主内容、页脚;课程列表有行有列;仪表板有多个大小不一的卡片。在 Grid 出现之前,开发者用浮动、定位和负 margin 模拟网格,代码脆弱且难以维护。
CSS Grid 是第一个专门为「二维布局」设计的 CSS 模块。CodeForge 的课程卡片矩阵、学习路线图的阶梯式布局、仪表盘的多卡片面板,都是 Grid 的天然应用场景。Grid 让「把这块区域放在第 2 行第 3 列」变成一句声明,而不是一堆计算和 hack。
先建立图像
一个 3×3 的 Grid 网格:
+----------+----------+----------+
| 1,1 | 1,2 | 1,3 | ← 行(row)
+----------+----------+----------+
| 2,1 | 2,2 | 2,3 |
+----------+----------+----------+
| 3,1 | 3,2 | 3,3 |
+----------+----------+----------+
↑ ↑ ↑
列1 列2 列3 ← 列(column)
Grid 线(grid line):行和列的边界线,从 1 开始编号
Grid 轨道(grid track):两条相邻线之间的空间(一行或一列)
Grid 单元格(grid cell):行和列交叉的最小单位
Grid 区域(grid area):一个或多个单元格组成的矩形区域
跟着做
步骤 1:创建基础网格
创建 grid.html,核心 CSS 如下:
:root { --primary: #2563eb; --surface: #fff; --border: #e2e8f0; --text: #0f172a; }
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, sans-serif; padding: 2rem; background: #f1f5f9; color: var(--text); }
.grid-container { display: grid; gap: 1rem; background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 1rem; }
.grid-item { background: var(--primary); color: white; padding: 1.5rem; border-radius: 8px; display: grid; place-items: center; font-weight: 500; }
页面骨架与之前课程相同,在 <body> 中放入以下三个演示区块:
<!-- 1. 三列等宽网格 -->
<div class="grid-container" style="grid-template-columns: 1fr 1fr 1fr;">
<div class="grid-item">A</div><div class="grid-item">B</div><div class="grid-item">C</div>
<div class="grid-item">D</div><div class="grid-item">E</div><div class="grid-item">F</div>
</div>
<!-- 2. 固定宽度 + 自适应 -->
<div class="grid-container" style="grid-template-columns: 200px 1fr 200px;">
<div class="grid-item">侧边栏 200px</div>
<div class="grid-item">主内容自适应</div>
<div class="grid-item">侧边栏 200px</div>
</div>
<!-- 3. 响应式自动填充 -->
<div class="grid-container" style="grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));">
<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>
打开页面,调整浏览器窗口宽度,观察「响应式自动填充」一节:当窗口变窄时,卡片自动从多列变为少列,无需任何媒体查询。repeat(auto-fill, minmax(200px, 1fr)) 的含义是:每列至少 200px,最多均分剩余空间,能放几列放几列。
步骤 2:使用 grid-template-areas 命名区域
<div class="grid-container" style="
grid-template-columns: 200px 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
'header header'
'sidebar main'
'footer footer';
min-height: 400px;
">
<div class="grid-item" style="grid-area: header;">Header</div>
<div class="grid-item" style="grid-area: sidebar;">Sidebar</div>
<div class="grid-item" style="grid-area: main;">Main Content</div>
<div class="grid-item" style="grid-area: footer;">Footer</div>
</div>
grid-template-areas 用 ASCII 艺术的方式直观定义布局结构。每个字符串代表一行,字符串中的每个词代表一个命名区域。. 表示空单元格。这是构建页面骨架最可读的方式。
步骤 3:控制项目位置与跨度
<div class="grid-container" style="grid-template-columns: repeat(4, 1fr);">
<div class="grid-item" style="grid-column: span 2;">跨 2 列</div>
<div class="grid-item">1 列</div>
<div class="grid-item">1 列</div>
<div class="grid-item" style="grid-column: span 2; grid-row: span 2;">跨 2×2</div>
<div class="grid-item">1 列</div>
<div class="grid-item">1 列</div>
<div class="grid-item" style="grid-column: 2 / 4;">从第 2 列线到第 4 列线</div>
</div>
grid-column: span 2:项目占 2 列宽度
grid-row: span 2:项目占 2 行高度
grid-column: 2 / 4:从第 2 条列线开始到第 4 条列线结束(即占第 2、3 列)
步骤 4:对齐控制
<div class="grid-container" style="
grid-template-columns: repeat(3, 100px);
justify-content: center;
height: 200px;
align-content: center;
">
<div class="grid-item">A</div>
<div class="grid-item">B</div>
<div class="grid-item">C</div>
</div>
justify-items / align-items:控制单元格内项目的对齐(类比 Flex 的 align-items)
justify-content / align-content:控制整个网格在容器内的对齐(当网格总尺寸小于容器时)
place-items: center 是 align-items: center; justify-items: center 的缩写
步骤 5:实战——CodeForge 课程卡片网格
.course-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1.5rem;
}
.course-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.course-card .cover {
height: 140px;
background: linear-gradient(135deg, var(--primary), #7c3aed);
display: grid;
place-items: center;
color: white;
font-weight: 600;
}
.course-card .body { padding: 1.25rem; flex: 1; display: flex; flex-direction: column; }
.course-card h3 { margin: 0 0 0.5rem; font-size: 1.125rem; }
.course-card p { margin: 0 0 1rem; color: #64748b; font-size: 0.9375rem; flex: 1; }
.course-card .footer { display: flex; justify-content: space-between; align-items: center; }
<div class="course-grid">
<article class="course-card">
<div class="cover">课程封面</div>
<div class="body">
<h3>HTML/CSS 基础</h3>
<p>掌握语义化标签、盒模型、Flexbox 与 Grid 布局。</p>
<div class="footer"><span style="color:var(--primary);font-weight:600;">免费</span><button>开始学习</button></div>
</div>
</article>
<!-- 更多卡片... -->
</div>
注意这个卡片内部用了 Flexbox(flex-direction: column + flex: 1 让按钮始终贴底),而卡片之间的排列用 Grid。这是典型的「Grid 管宏观,Flex 管微观」的组合模式。
讲清楚要点
-
Grid 是二维布局系统:与 Flexbox 的一维模型不同,Grid 同时控制行和列。你可以精确地说「这个元素放在第 2 行第 3 列」,而不必关心 HTML 源码顺序。这是布局思维的根本转变。
-
fr 单位是 Grid 的灵魂:fr(fraction,剩余空间份数)让列宽按比例分配,自动适应容器大小。1fr 2fr 1fr 表示三列按 1:2:1 分配。fr 与 minmax() 结合可实现既灵活又有下限的响应式轨道。
-
grid-template-areas 提升可维护性:对于整体页面布局,用命名区域比数行列号更直观。修改布局时只需调整 ASCII 图,不用重新计算每个项目的 grid-column 和 grid-row。配合媒体查询,可以轻松实现「移动端单列,桌面端多列」。
-
自动放置算法:未显式指定位置的项目,Grid 会按源码顺序自动填充空白单元格。grid-auto-flow: dense 可以让后续项目回填前面留下的空隙,适合卡片大小不一的 masonry 式布局。
-
auto-fill vs auto-fit:auto-fill 在轨道不足时保留空轨道(可能产生空白列);auto-fit 会折叠空轨道,让现有项目拉伸占满。响应式卡片网格通常用 auto-fit。
-
Grid 与 Flex 的边界:Grid 擅长「由外向内」的布局——先定义网格骨架,再把内容放进去。Flex 擅长「由内向外」的布局——内容决定尺寸,排列随内容变化。两者互补,不是替代关系。
你可能会懵的地方
-
「Grid 和 Flexbox 到底哪个更好?」
不是竞争关系。Grid 管二维页面骨架,Flex 管一维组件内部。一个页面通常外层用 Grid 划分区域,区域内用 Flex 排列元素。例如:Grid 定义「侧边栏 + 主内容」,主内容内部的按钮组用 Flex 排列。
-
「grid-template-columns: repeat(3, 1fr) 和 grid-template-columns: 1fr 1fr 1fr 有区别吗?」
功能上完全一样。repeat(3, 1fr) 是简写,避免重复书写。当列数很多或模式复杂时(如 repeat(3, 1fr 2fr) 表示 6 列:1fr, 2fr, 1fr, 2fr, 1fr, 2fr),repeat() 的可读性优势明显。
-
「为什么我的 Grid 项目没有按预期位置放置?」
检查是否显式指定了 grid-column 或 grid-row。如果没有,Grid 的自动放置算法会按源码顺序填充。如果项目有 span 导致无法放入当前行,算法会跳到下一行,可能留下空隙。用 grid-auto-flow: dense 可让后续项目回填空隙。
-
「Grid 能做瀑布流(Masonry)吗?」
纯 CSS Grid 的 grid-auto-flow: dense 可以近似,但列高由整行最高项目决定,不是真正的 Masonry。真正的 Masonry 需要 grid-template-rows: masonry(Firefox 实验性支持)或 JavaScript 库。对于等高卡片,Grid 完全够用。
-
「Grid 的浏览器兼容性如何?」
现代浏览器(Chrome、Firefox、Safari、Edge)自 2017 年起全面支持 Grid。IE11 有旧版 Grid 规范(-ms- 前缀),但语法差异大,不建议为 IE11 使用 Grid。如果需要兼容旧浏览器,可以用 @supports 查询提供 Flexbox 回退。
错法对照
| 错法 | 问题 | 正解 |
|---|
| 用 Grid 做简单的水平按钮组 | 大材小用,代码复杂 | 用 Flexbox |
| 用浮动或定位模拟网格 | 脆弱,需要清除浮动,响应式困难 | 用 Grid 的 grid-template-columns |
| 硬编码列数,窗口变窄时溢出 | 需要大量媒体查询 | 用 repeat(auto-fit, minmax(250px, 1fr)) |
给每个项目写 grid-column: 1 / 2 等精确位置 | 维护困难,插入新项目时要重新编号 | 用 grid-template-areas 或让自动放置算法处理 |
忽略 gap 用 margin 做间距 | 边缘项目需要特殊处理 | 用容器的 gap 属性 |
| 在 Grid 项目中继续用浮动 | 破坏 Grid 布局,产生意外结果 | Grid 项目内部用正常流、Flex 或嵌套 Grid |
卡点急救
-
Grid 项目之间没有间距:检查是否给容器设置了 gap。Grid 的 gap 同时作用于行和列(row-gap 和 column-gap 的缩写)。旧代码可能用 grid-gap,现代浏览器已支持无前缀的 gap。
-
响应式网格在窄屏下出现横向滚动条:minmax() 的最小值可能太大,或某个项目内容(如长单词、表格)太宽。检查 minmax(200px, 1fr) 的 200px 是否超过手机屏幕宽度;给项目内容加 overflow-wrap: break-word;或给网格项目加 min-width: 0。
-
想让某个项目跨越多行多列,但后面的项目位置乱了:这是自动放置算法的正常行为。如果项目有 span 或显式位置,后续项目会寻找下一个可用空位。如需精确控制,给所有关键项目都显式指定 grid-column 和 grid-row,或使用 grid-template-areas 统一定义。
面试常问
-
「Grid 和 Flexbox 的主要区别?如何选择?」
Grid 是二维布局系统,同时控制行和列,适合整体页面骨架和卡片矩阵。Flexbox 是一维布局系统,沿单一方向排列,适合导航栏、按钮组、表单行等线性排列。选择标准:需要同时控制行和列时用 Grid;只需要沿一个方向排列时用 Flex。实际项目中两者常嵌套使用:Grid 定义页面骨架,Flex 定义组件内部。
-
「fr 单位的含义是什么?」
fr 是 fraction(份数)的缩写,表示剩余空间的比例分配。grid-template-columns: 1fr 2fr 1fr 表示三列按 1:2:1 分配可用空间。fr 与 minmax() 结合可实现弹性响应式轨道,如 minmax(200px, 1fr) 表示轨道至少 200px,最多参与剩余空间均分。
-
「如何实现一个响应式卡片网格,不写媒体查询?」
使用 grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))。auto-fit 根据容器宽度自动计算能放几列,minmax(280px, 1fr) 确保每列至少 280px、最多均分剩余空间。窗口变窄时列数自动减少,变宽时自动增加,完全无需媒体查询。
自测三问
grid-template-columns: repeat(3, 1fr) 和 grid-template-columns: 1fr 1fr 1fr 有什么区别?repeat(auto-fill, minmax(200px, 1fr)) 又有什么不同?
- 在
grid-template-areas 中,. 代表什么?如果一个区域在 ASCII 图中占据多个单元格,这些单元格必须是什么形状?
- 你想实现一个页面布局:顶部 header 通栏,左侧 sidebar 固定 250px,右侧 main 自适应,底部 footer 通栏。请写出
grid-template-areas 和 grid-template-columns/rows 的声明。
小结一句话
CSS Grid 用「先画棋盘,再放棋子」的二维思维,让复杂页面布局从「计算和 hack」变成「声明和放置」;fr 单位、minmax() 和 auto-fit 的组合,更是让响应式网格无需一行媒体查询。
延伸