Grid 布局
问题
CSS Grid 布局的核心概念是什么?如何定义网格轨道、区域?Grid 和 Flex 的适用场景有什么区别?
答案
Grid 布局概述
CSS Grid(网格布局)是一种二维布局系统,可以同时控制行和列。它是 CSS 中最强大的布局工具,特别适合实现复杂的页面布局。
启用 Grid 布局
.container {
display: grid; /* 块级 Grid 容器 */
/* 或 */
display: inline-grid; /* 行内 Grid 容器 */
}
核心术语
col 1 col 2 col 3
┌─────────┬─────────┬─────────┐ ← 网格线 1
row 1│ cell │ cell │ cell │
├─────────┼─────────┼─────────┤ ← 网格线 2
row 2│ cell │ cell │ cell │
├─────────┼─────────┼─────────┤ ← 网格线 3
row 3│ cell │ cell │ cell │
└─────────┴─────────┴─────────┘ ← 网格线 4
↑ ↑ ↑ ↑
线 1 线 2 线 3 线 4
| 术语 | 说明 |
|---|---|
| Grid Container | 设置 display: grid 的元素 |
| Grid Item | 容器的直接子元素 |
| Grid Line | 网格线,定义行和列的边界 |
| Grid Track | 两条相邻网格线之间的空间(一行或一列) |
| Grid Cell | 行与列交叉形成的最小单元格 |
| Grid Area | 一个或多个 cell 组成的矩形区域 |
| Grid Gap | 行/列之间的间距 |
容器属性
1. 定义行和列 — grid-template-columns / grid-template-rows
定义网格轨道
.container {
display: grid;
/* 固定值 */
grid-template-columns: 200px 1fr 200px;
/* fr 单位:按比例分配剩余空间 */
grid-template-columns: 1fr 2fr 1fr;
/* repeat() 函数 */
grid-template-columns: repeat(3, 1fr); /* 3 列等分 */
grid-template-columns: repeat(3, 100px 200px); /* 重复模式 */
/* auto-fill / auto-fit:自动填充 */
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
/* minmax():最小最大值 */
grid-template-columns: minmax(100px, 300px) 1fr;
/* auto:由内容决定 */
grid-template-columns: auto 1fr auto;
}
fr 单位
fr(fraction)单位表示剩余空间的分数。与 Flex 的 flex-grow 类似,但作用在网格轨道上:
grid-template-columns: 200px 1fr 2fr;
/* 200px 固定列 + 剩余空间的 1/3 + 剩余空间的 2/3 */
2. auto-fill vs auto-fit
两者在有剩余空间时表现不同:
/* auto-fill:尽可能填充列,多余空间留空 */
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
/* auto-fit:尽可能填充列,多余空间拉伸现有列 */
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
容器 600px,每列 minmax(150px, 1fr),只有 2 个项目:
auto-fill: |■ 150px|■ 150px| 空 | 空 | (创建了 4 列)
auto-fit: |■ 300px |■ 300px | (2 列拉伸填满)
3. 网格区域 — grid-template-areas
使用命名区域实现直观的布局:
区域命名布局
.container {
display: grid;
grid-template-columns: 200px 1fr 200px;
grid-template-rows: 60px 1fr 50px;
grid-template-areas:
"header header header"
"sidebar main aside"
"footer footer footer";
gap: 10px;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.aside { grid-area: aside; }
.footer { grid-area: footer; }
┌────────────────────────────────────┐
│ header │
├────────┬──────────────────┬────────┤
│sidebar │ main │ aside │
├────────┴──────────────────┴────────┤
│ footer │
└────────────────────────────────────┘
空单元格
使用 . 表示空白区域:
grid-template-areas:
"header header header"
"sidebar main ."
"footer footer footer";
4. 间距 — gap
.container {
gap: 20px; /* 行和列间距都是 20px */
gap: 20px 10px; /* 行间距 20px,列间距 10px */
row-gap: 20px; /* 仅行间距 */
column-gap: 10px; /* 仅列间距 */
}
5. 对齐方式
| 属性 | 作用对象 | 方向 | 控制的是 |
|---|---|---|---|
justify-items | 所有 item | 行方向(水平) | item 在 cell 内的对齐 |
align-items | 所有 item | 列方向(垂直) | item 在 cell 内的对齐 |
place-items | 简写 | 两个方向 | align-items + justify-items |
justify-content | 整个网格 | 行方向 | 网格在容器内的对齐 |
align-content | 整个网格 | 列方向 | 网格在容器内的对齐 |
place-content | 简写 | 两个方向 | align-content + justify-content |
居中所有项目
.container {
display: grid;
place-items: center; /* 每个 item 在自己的 cell 内居中 */
}
6. 隐式网格 — grid-auto-rows / grid-auto-columns
当项目超出定义的网格时,自动创建的行/列大小:
.container {
grid-template-rows: 100px 100px; /* 只定义了 2 行 */
grid-auto-rows: 60px; /* 多出的行高度为 60px */
grid-auto-flow: row; /* 默认:按行填充 */
grid-auto-flow: column; /* 按列填充 */
grid-auto-flow: dense; /* 密集填充(填补空白) */
}
项目属性
1. 指定位置 — grid-column / grid-row
项目定位
.item {
/* 从第 1 条列线到第 3 条列线(跨 2 列) */
grid-column: 1 / 3;
grid-column: 1 / span 2; /* 等价写法 */
/* 从第 1 条行线到第 3 条行线(跨 2 行) */
grid-row: 1 / 3;
grid-row: 1 / span 2; /* 等价写法 */
}
2. 命名区域 — grid-area
.item {
grid-area: header; /* 引用命名区域 */
grid-area: 1 / 1 / 3 / 3; /* row-start / col-start / row-end / col-end */
}
3. 单个项目对齐 — justify-self / align-self
.item {
justify-self: center; /* 水平方向居中 */
align-self: end; /* 垂直方向底部对齐 */
place-self: center; /* 两个方向居中 */
}
实用函数
| 函数 | 说明 | 示例 |
|---|---|---|
repeat(n, size) | 重复 n 次轨道 | repeat(3, 1fr) |
minmax(min, max) | 最小最大约束 | minmax(200px, 1fr) |
fit-content(max) | 由内容决定,不超过 max | fit-content(300px) |
auto-fill | 尽可能多地填充列 | repeat(auto-fill, minmax(200px, 1fr)) |
auto-fit | 填充列并拉伸 | repeat(auto-fit, minmax(200px, 1fr)) |
Grid vs Flex 对比
| 特性 | Flex | Grid |
|---|---|---|
| 布局维度 | 一维(行或列) | 二维(行和列) |
| 适用场景 | 组件内部元素排列 | 页面整体布局 |
| 内容驱动 vs 布局驱动 | 内容驱动(内容决定大小) | 布局驱动(先定义网格) |
| 对齐能力 | 主轴 + 交叉轴 | 行 + 列,更精细 |
| 区域命名 | 不支持 | 支持 grid-template-areas |
| 项目重叠 | 不支持 | 支持(通过网格线定位) |
| 兼容性 | 非常好 | 很好(IE 11 部分支持) |
最佳实践
- Flex 用于组件级:导航栏、卡片内部、按钮组、列表项
- Grid 用于页面级:整体页面结构、卡片网格、仪表盘
- 两者可以嵌套使用:Grid 做外层布局,Flex 做内部排列
响应式 Grid 布局
无媒体查询的响应式网格
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 24px;
}
/* 容器宽度足够时自动增加列数,不足时自动减少 */
这是最优雅的响应式方案之一,无需任何媒体查询即可实现自适应列数。
常见面试问题
Q1: Grid 布局的核心概念有哪些?
答案:
核心概念包括:
- Grid Container / Item:容器和直接子元素
- Grid Line:网格线,用数字或命名引用
- Grid Track:一行或一列
- Grid Cell:最小单元格
- Grid Area:一个或多个 cell 组成的矩形区域
- fr 单位:可用空间的比例份额
- gap:行列间距
Q2: fr 单位是什么?和百分比有什么区别?
答案:
fr 代表 fraction(分数),表示剩余空间的比例:
| 特性 | fr | % |
|---|---|---|
| 参考基准 | 剩余可用空间 | 容器总宽度 |
| 是否考虑 gap | 是(自动扣除) | 否(可能溢出) |
| 溢出风险 | 低 | 高(如 3 × 33.33% + gap 会溢出) |
/* 使用百分比:加上 gap 后会溢出 */
grid-template-columns: 33.33% 33.33% 33.33%; /* + gap = 超出 100% */
/* 使用 fr:自动扣除 gap 后分配 */
grid-template-columns: 1fr 1fr 1fr; /* 完美 */
Q3: auto-fill 和 auto-fit 的区别?
答案:
两者在项目数量少于可填充列数时表现不同:
auto-fill:创建尽可能多的列轨道,多余轨道留空auto-fit:创建列轨道后,多余轨道折叠为 0,已有项目拉伸填满
项目数量恰好填满或大于列数时,两者表现一致。
Q4: 如何实现一个响应式卡片网格(无媒体查询)?
答案:
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
原理:
auto-fit:自动计算每行能放几列minmax(250px, 1fr):每列最少 250px,最大可拉伸- 宽屏自动多列,窄屏自动减少列数
Q5: grid-template-areas 有什么用?有什么限制?
答案:
用途:通过命名区域直观地定义布局,代码即所见。
限制:
- 区域必须是矩形,不能是 L 形或不规则形状
- 区域必须是连续的,不能有间隔
- 区域名不能使用保留字
- 使用
.表示空白单元格
/* ✓ 合法 */
grid-template-areas:
"a a b"
"a a c"
"d d d";
/* ✗ 非法 —— a 不是矩形 */
grid-template-areas:
"a b b"
"a a c"
"d d d";
Q6: Grid 中如何让某个项目跨多行或多列?
答案:
/* 方法一:指定起止网格线 */
.item {
grid-column: 1 / 3; /* 从第 1 条到第 3 条列线 */
grid-row: 1 / 4; /* 从第 1 条到第 4 条行线 */
}
/* 方法二:span 关键字 */
.item {
grid-column: span 2; /* 跨 2 列 */
grid-row: span 3; /* 跨 3 行 */
}
/* 方法三:命名区域 */
.item {
grid-area: main; /* 在 grid-template-areas 中定义跨越范围 */
}
Q7: 什么是隐式网格?
答案:
- 显式网格:通过
grid-template-columns/rows定义的网格 - 隐式网格:当项目数量超出显式网格时,浏览器自动创建的额外行/列
.container {
grid-template-columns: repeat(3, 1fr);
grid-template-rows: 100px 100px; /* 只定义了 2 行 */
grid-auto-rows: 50px; /* 第 3 行及以后的高度 */
}
如果有 9 个项目(3 行),第 3 行就是隐式行,高度为 grid-auto-rows 的值。
Q8: place-items 和 place-content 的区别?
答案:
| 属性 | 作用 | 等价简写 |
|---|---|---|
place-items | 每个 item 在其 cell 内的对齐 | align-items + justify-items |
place-content | 整个网格在容器内的对齐 | align-content + justify-content |
/* 网格容器比网格大时才有区别 */
.container {
display: grid;
width: 800px;
height: 600px;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(2, 100px);
place-items: center; /* 每个 item 在 100×100 的 cell 内居中 */
place-content: center; /* 整个 300×200 的网格在 800×600 容器中居中 */
}
Q9: grid-auto-flow: dense 是什么效果?
答案:
dense 会使用密集填充算法,尝试填补网格中的空白:
.container {
grid-auto-flow: dense;
}
正常流中,如果某个项目跨了多列导致该行有空位,后续小项目会回填到前面的空位中。适用于瀑布流、图片画廊等场景。
注意:dense 会改变项目的视觉顺序,可能影响可访问性。
Q10: Flex 和 Grid 怎么选?
答案:
| 场景 | 推荐 | 原因 |
|---|---|---|
| 导航栏项目排列 | Flex | 一维、内容驱动 |
| 卡片网格 | Grid | 二维、需要对齐行列 |
| 表单布局 | Grid | 标签和输入框需要对齐 |
| 等高列 | 都可以 | Grid 更直接 |
| 页面整体结构 | Grid | header/sidebar/main/footer |
| 按钮组 | Flex | 简单一维排列 |
| 仪表盘 | Grid | 复杂二维 + 合并区域 |
经验法则:从内向外用 Flex,从外向内用 Grid。