Grid 布局
为什么 Grid 是二维布局的终极方案
CSS Grid Layout 是 CSS 中第一个为二维布局设计的模型。与 Flexbox 的一维控制不同,Grid 允许同时在行和列两个维度上精确定义布局结构。
Grid 的核心理念是布局优先(layout-first):先定义网格结构,再将内容放置到网格中。这与 Flexbox 的内容优先(content-first)形成互补。
面试定位:Grid 布局是高级前端面试的考察重点。面试官通过 fr 单位的分配机制、auto-fill 与 auto-fit 的区别、Subgrid 的使用场景等问题,判断候选人对现代 CSS 布局体系的掌握深度。
核心概念
Grid 术语
Grid Container
┌──────────────────────────────────────────────┐
│ Column 1 Column 2 Column 3 │
│ Line 1 ─────────┬───────────┬───────────┐ │
│ │ Cell │ Cell │ Cell │ │ Row 1
│ Line 2 ├─────────┼───────────┼───────────┤ │
│ │ Cell │ Area │ │ │ Row 2
│ Line 3 ├─────────┤ (spans │ │ │
│ │ Cell │ 2×2) │ │ │ Row 3
│ Line 4 └─────────┴───────────┴───────────┘ │
│ Line 1 Line 2 Line 3 Line 4 │
└──────────────────────────────────────────────┘
| 术语 | 含义 |
|---|---|
| Grid Container | 设置 display: grid 的元素 |
| Grid Item | Grid Container 的直接子元素 |
| Grid Line | 网格线,编号从 1 开始 |
| Grid Track | 两条相邻网格线之间的行或列 |
| Grid Cell | 最小的网格单元(一行一列的交叉区域) |
| Grid Area | 一个或多个 Grid Cell 组成的矩形区域 |
显式网格定义
grid-template-columns / grid-template-rows
.grid {
display: grid;
/* 固定尺寸 */
grid-template-columns: 200px 1fr 200px;
/* 百分比 */
grid-template-columns: 25% 50% 25%;
/* fr 单位(Fractional Unit) */
grid-template-columns: 1fr 2fr 1fr;
/* 混合 */
grid-template-columns: 250px 1fr minmax(200px, 400px);
/* repeat() 函数 */
grid-template-columns: repeat(3, 1fr);
grid-template-columns: repeat(4, 1fr 2fr); /* 8 列:1fr 2fr 1fr 2fr ... */
/* 行定义 */
grid-template-rows: auto 1fr auto; /* header - content - footer */
}
fr 单位的分配机制
fr 表示可用空间的等比份额。分配过程:
- 先计算固定尺寸(px、%、auto、min-content 等)
- 剩余空间按 fr 比例分配
.grid {
width: 1200px;
grid-template-columns: 200px 2fr 1fr;
gap: 20px;
/*
* 固定消耗:200px + 20px*2 = 240px
* 剩余空间:1200 - 240 = 960px
* 2fr = 960 * 2/3 = 640px
* 1fr = 960 * 1/3 = 320px
*/
}
fr vs %:fr 自动扣除 gap 后分配,% 基于容器总宽度不扣 gap。因此 repeat(3, 1fr) 配合 gap 不会溢出,而 repeat(3, 33.33%) 加上 gap 会溢出。
minmax() 函数
.grid {
/* 列最小 200px,最大 1fr */
grid-template-columns: repeat(3, minmax(200px, 1fr));
/* 行最小 100px,最大 auto(内容决定) */
grid-template-rows: minmax(100px, auto);
/* 侧边栏:最小 200px,最大 300px */
grid-template-columns: minmax(200px, 300px) 1fr;
}
命名网格线
.grid {
grid-template-columns:
[sidebar-start] 250px
[sidebar-end content-start] 1fr
[content-end];
grid-template-rows:
[header-start] auto
[header-end main-start] 1fr
[main-end footer-start] auto
[footer-end];
}
.sidebar {
grid-column: sidebar-start / sidebar-end;
grid-row: header-end / footer-start;
}
grid-template-areas —— 命名区域
.layout {
display: grid;
grid-template-areas:
"header header header"
"nav content sidebar"
"footer footer footer";
grid-template-columns: 200px 1fr 250px;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
}
header { grid-area: header; }
nav { grid-area: nav; }
main { grid-area: content; }
aside { grid-area: sidebar; }
footer { grid-area: footer; }
/* 响应式:移动端变为单列 */
@media (max-width: 768px) {
.layout {
grid-template-areas:
"header"
"nav"
"content"
"sidebar"
"footer";
grid-template-columns: 1fr;
}
}
.(句点) 表示空单元格:
grid-template-areas:
"header header ."
"nav content sidebar"
". footer footer";
隐式网格与自动放置
显式 vs 隐式网格
当 Grid items 超出 grid-template-* 定义的范围时,浏览器自动创建隐式轨道(implicit tracks):
.grid {
grid-template-columns: repeat(3, 1fr);
grid-template-rows: 200px 200px;
/* 如果有 > 6 个子元素,第 7 个开始进入隐式行 */
/* 控制隐式轨道尺寸 */
grid-auto-rows: minmax(150px, auto);
grid-auto-columns: 200px;
}
grid-auto-flow —— 自动放置算法
.grid {
grid-auto-flow: row; /* 默认:按行填充 */
grid-auto-flow: column; /* 按列填充 */
grid-auto-flow: dense; /* 紧密填充(回填空白) */
grid-auto-flow: row dense; /* 按行 + 紧密填充 */
}
dense 的作用:默认放置算法遇到大元素时会留下空白,dense 允许后续小元素回填这些空白。适合瀑布流式的不规则网格,但会改变视觉顺序(与 DOM 顺序不一致)。
Grid 项目放置
线编号放置
.item {
grid-column-start: 1;
grid-column-end: 3; /* 跨越第 1 到第 3 条列线 = 2 列 */
grid-row-start: 1;
grid-row-end: 2;
/* 简写 */
grid-column: 1 / 3; /* start / end */
grid-row: 1 / 2;
/* span 关键字 */
grid-column: 1 / span 2; /* 从第 1 条线开始,跨 2 列 */
grid-column: span 2; /* 自动放置,跨 2 列 */
/* 负数:从末尾计算 */
grid-column: 1 / -1; /* 跨越所有列 */
/* grid-area 简写:row-start / col-start / row-end / col-end */
grid-area: 1 / 1 / 3 / 3;
}
对齐体系
Grid 的对齐分为两个维度和两个层级:
| 属性 | 作用对象 | 轴 | 设置在 |
|---|---|---|---|
justify-items |
所有 items | 行内轴(水平) | Container |
align-items |
所有 items | 块轴(垂直) | Container |
place-items |
简写 | 两轴 | Container |
justify-self |
单个 item | 行内轴 | Item |
align-self |
单个 item | 块轴 | Item |
place-self |
简写 | 两轴 | Item |
justify-content |
整个网格 | 行内轴 | Container |
align-content |
整个网格 | 块轴 | Container |
place-content |
简写 | 两轴 | Container |
.grid {
display: grid;
place-items: center; /* 所有 items 在 cell 内居中 */
place-content: center; /* 整个网格在容器内居中 */
}
.item {
place-self: end center; /* 此 item:垂直底部,水平居中 */
}
响应式 Grid 模式
auto-fill vs auto-fit
/* auto-fill: 尽可能多地创建列,即使列是空的 */
.grid-fill {
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
/* auto-fit: 与 auto-fill 类似,但空列会折叠为 0 宽度 */
.grid-fit {
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
关键区别:当容器足够宽、项目较少时:
auto-fill:保留空列的空间(创建了隐式列轨道)auto-fit:空列折叠,已有项目会拉伸填满剩余空间
容器 1000px,3 个 200px 的项目:
auto-fill: [200px][200px][200px][200px][200px] ← 5 列(2 空列占位)
auto-fit: [333px][333px][333px] ← 3 列(空列折叠,项目拉伸)
无 Media Query 的响应式网格
/* 经典的响应式卡片网格 */
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
gap: 1.5rem;
}
min(300px, 100%) 解决了小屏幕(< 300px)时 minmax 溢出的问题。
Subgrid
什么是 Subgrid
Subgrid 让嵌套的 Grid 容器继承父 Grid 的轨道定义,解决了嵌套网格的对齐问题:
.parent {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto auto auto;
gap: 1rem;
}
.child {
grid-column: 1 / -1; /* 跨越所有列 */
display: grid;
grid-template-columns: subgrid; /* 继承父级的列定义 */
grid-template-rows: subgrid; /* 继承父级的行定义 */
}
Subgrid 的典型场景
卡片列表中的内容对齐:
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
}
.card {
display: grid;
grid-template-rows: subgrid; /* 继承行轨道 */
grid-row: span 3; /* 每张卡片占 3 行 */
}
/* 所有卡片的标题、内容、按钮自动对齐 */
.card h3 { /* 第 1 行 */ }
.card p { /* 第 2 行 */ }
.card button { align-self: end; /* 第 3 行 */ }
浏览器支持(2025):Chrome 117+、Firefox 71+(最早支持)、Safari 16+。Baseline 2023。
Grid 高级技巧
层叠(Overlapping)
Grid items 可以占据相同的 cell 区域,实现层叠效果:
.hero {
display: grid;
grid-template: 1fr / 1fr;
}
.hero img,
.hero .overlay {
grid-area: 1 / 1; /* 占据同一 cell */
}
.hero .overlay {
background: linear-gradient(transparent, rgba(0,0,0,0.7));
display: flex;
align-items: end;
padding: 2rem;
color: white;
}
Masonry Layout(CSS 原生瀑布流)
CSS Grid Level 3 规范草案中提出了原生瀑布流支持:
/* 规范草案——尚未广泛支持 */
.masonry {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-template-rows: masonry; /* 行方向瀑布流 */
}
当前状态(2026):仅 Firefox 在 flag 下支持。Chrome 团队正在考虑替代方案(display: masonry)。生产环境仍需 JavaScript 库(如 Masonry.js)。
带 Grid 的 Full-bleed 布局
.full-bleed-layout {
display: grid;
grid-template-columns:
[full-start] 1fr
[content-start] min(65ch, 100% - 4rem)
[content-end] 1fr
[full-end];
}
.full-bleed-layout > * {
grid-column: content;
}
.full-bleed-layout > .full-width {
grid-column: full;
}
实战:页面级布局
经典 Holy Grail 布局
.page {
display: grid;
grid-template:
"header header header" auto
"nav main aside" 1fr
"footer footer footer" auto
/ 200px 1fr 250px;
min-height: 100vh;
gap: 0;
}
@media (max-width: 768px) {
.page {
grid-template:
"header" auto
"nav" auto
"main" 1fr
"aside" auto
"footer" auto
/ 1fr;
}
}
Dashboard 网格
.dashboard {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: minmax(120px, auto);
gap: 1rem;
}
.widget-large {
grid-column: span 2;
grid-row: span 2;
}
.widget-wide {
grid-column: span 2;
}
.widget-tall {
grid-row: span 2;
}
面试高频问题
Q: auto-fill 和 auto-fit 的区别?
回答要点:两者在 repeat() 中都会自动计算列数。区别在于项目不足以填满容器时:auto-fill 保留空列轨道的空间;auto-fit 将空列折叠为 0 宽度,让已有项目拉伸填满。通常 auto-fit 更符合响应式需求。
Q: fr 单位是怎么分配空间的?
回答要点:fr 分配的是剩余空间。浏览器先计算固定尺寸(px、%、auto、min-content 等)和 gap,然后将剩余空间按 fr 比例分配。这也是 fr 与 % 的关键区别——% 基于容器总宽度不扣 gap,fr 会自动扣除。
Q: 什么是 Subgrid?解决什么问题?
回答要点:Subgrid 让嵌套的 Grid 容器继承父级的轨道定义。解决的核心问题是嵌套网格的对齐——比如卡片列表中,每张卡片内部的标题、内容、按钮需要跨卡片水平对齐。没有 Subgrid 时,每张卡片是独立的 Grid,无法共享轨道。