跳到主要内容

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)由内容决定,不超过 maxfit-content(300px)
auto-fill尽可能多地填充列repeat(auto-fill, minmax(200px, 1fr))
auto-fit填充列并拉伸repeat(auto-fit, minmax(200px, 1fr))

Grid vs Flex 对比

特性FlexGrid
布局维度一维(行或列)二维(行和列)
适用场景组件内部元素排列页面整体布局
内容驱动 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-fillauto-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 有什么用?有什么限制?

答案

用途:通过命名区域直观地定义布局,代码即所见。

限制:

  1. 区域必须是矩形,不能是 L 形或不规则形状
  2. 区域必须是连续的,不能有间隔
  3. 区域名不能使用保留字
  4. 使用 . 表示空白单元格
/* ✓ 合法 */
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-itemsplace-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 更直接
页面整体结构Gridheader/sidebar/main/footer
按钮组Flex简单一维排列
仪表盘Grid复杂二维 + 合并区域

经验法则:从内向外用 Flex,从外向内用 Grid

相关链接