1. Grid 到底解决什么问题

在网页里,布局工具大致有三类思路:

  • 普通文档流:元素从上往下排
  • Flex:一维布局,主要处理“一行”或者“一列”
  • Grid:二维布局,同时处理“多行 + 多列” ([MDN Web Docs][1])

所以一句话记住:

Flex 适合排一条线,Grid 适合排一张网。

例如:

  • 导航栏一排按钮:Flex 更顺手
  • 整个页面分头部、侧边栏、主内容、底部:Grid 更合适
  • 商品卡片九宫格:Grid 更合适
  • 仪表盘模块布局:Grid 更合适

2. Grid 的核心思维

Grid 的本质就两步:

  1. 先把父元素定义成网格容器
  2. 再定义列、行,以及子元素怎么摆进去

最基础的写法:

1
2
3
.container {
display: grid;
}

只要写了这个,.container 就成了 grid container,它的直接子元素就变成 grid items。Grid 是通过 display: grid 启用的。


3. 最小可运行示例

先看最简单的例子:

1
2
3
4
5
6
<div class="container">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
</div>
1
2
3
4
5
6
.container {
display: grid;
grid-template-columns: 100px 100px;
grid-template-rows: 80px 80px;
gap: 10px;
}

这段代码的意思:

  • display: grid:启用 Grid
  • grid-template-columns: 100px 100px:定义两列,每列 100px
  • grid-template-rows: 80px 80px:定义两行,每行 80px
  • gap: 10px:格子之间间距 10px

结果就是一个 2 列 2 行 的网格。


4. 先学最重要的 4 个属性

4.1 display: grid

开启 Grid。

1
2
3
.container {
display: grid;
}

4.2 grid-template-columns

定义列。

1
2
3
4
.container {
display: grid;
grid-template-columns: 200px 200px 200px;
}

表示 3 列,每列 200px。grid-template-columns 就是定义列轨道尺寸。

也可以这样写:

1
grid-template-columns: 1fr 1fr 1fr;

这里的 fr 是 Grid 里最常用的单位之一,表示 按比例分剩余空间

比如:

1
grid-template-columns: 1fr 2fr 1fr;

意思是:

  • 第一列占 1 份
  • 第二列占 2 份
  • 第三列占 1 份

总共 4 份,所以中间那列最宽。


4.3 grid-template-rows

定义行。

1
grid-template-rows: 100px 200px;

表示两行:

  • 第一行 100px
  • 第二行 200px

4.4 gap

定义网格间距。

1
gap: 16px;

等价于:

1
2
row-gap: 16px;
column-gap: 16px;

如果想分别控制:

1
2
row-gap: 20px;
column-gap: 10px;

5. fr 是 Grid 里最关键的单位

初学 Grid,最容易卡的就是 fr

可以把它理解为:

剩余空间分配单位

看例子:

1
2
3
4
.container {
display: grid;
grid-template-columns: 200px 1fr 1fr;
}

含义:

  • 第一列固定 200px
  • 后面两列平分剩余空间

再看:

1
grid-template-columns: 1fr 2fr 1fr;

含义:

  • 总份数 = 1 + 2 + 1 = 4
  • 第二列拿 2/4,也就是一半
  • 两边各拿 1/4

实际开发里,fr 用得非常多,因为它天然适合响应式布局。MDN 的 Grid 教程也把它作为基础能力来介绍。


6. repeat():别重复手写

比如你想写 4 列,每列都一样宽:

1
grid-template-columns: 1fr 1fr 1fr 1fr;

可以简写成:

1
grid-template-columns: repeat(4, 1fr);

再比如:

1
grid-template-columns: repeat(3, 200px);

表示 3 列,每列 200px。

这是最常见写法之一。


7. Grid 里的“线”概念

Grid 不只是“格子”,更准确说是由很多 线 划出来的。

例如:

1
grid-template-columns: repeat(3, 1fr);

3 列其实会有 4 条竖线

  • 第 1 条线:最左边
  • 第 2 条线:第 1 列和第 2 列之间
  • 第 3 条线:第 2 列和第 3 列之间
  • 第 4 条线:最右边

行也是同理。

这个概念很重要,因为很多定位其实不是“放到第几个格子”,而是:

从第几条线开始,到第几条线结束。


8. 子元素怎么指定位置

假设有这样一个容器:

1
2
3
4
5
6
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: 100px 100px;
gap: 10px;
}

子元素可以这样指定:

1
2
3
4
.item1 {
grid-column: 1 / 3;
grid-row: 1 / 2;
}

意思是:

  • 从第 1 条列线开始,到第 3 条列线结束
  • 所以横向占 2 列
  • 从第 1 条行线开始,到第 2 条行线结束
  • 所以纵向占 1 行

简化理解:

1
2
grid-column: start / end;
grid-row: start / end;

比如:

1
2
3
.item2 {
grid-column: 2 / 4;
}

表示从第 2 条列线到第 4 条列线,也就是占第 2、3 两列。


9. span:更符合直觉

有时候你不想写“到第几条线结束”,你只想说“占几列”。

这时用 span

1
2
3
.item {
grid-column: span 2;
}

表示:

这个元素横向占 2 列。

再比如:

1
2
3
.item {
grid-row: span 3;
}

表示纵向占 3 行。


10. 自动摆放机制

如果你不给子元素写位置,Grid 会自动排。

比如:

1
2
3
4
5
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
}

里面放 7 个元素,浏览器会自动这样排:

  • 第一行 3 个
  • 第二行 3 个
  • 第三行 1 个

这叫 auto-placement,也就是自动布局。MDN 也专门把它列为 Grid 的核心概念。(place 放置 /pleɪs/ ; ment 状态,结果 /mənt/)

如果你给某些元素指定了跨列、跨行,自动摆放会继续补空位。


11. justify-itemsalign-itemsplace-items

这组属性是控制 格子里的内容怎么对齐

假设每个格子都比内容大,就需要决定内容放哪里。

11.1 水平方向

1
2
3
4
justify-items: start;
justify-items: center;
justify-items: end;
justify-items: stretch;

11.2 垂直方向

1
2
3
4
align-items: start;
align-items: center;
align-items: end;
align-items: stretch;

11.3 合并写法

1
place-items: center;

等价于:

1
2
justify-items: center;
align-items: center;

12. justify-contentalign-content 跟上面不是一回事

  • justify-items / align-items:控制 单元格内部内容
  • justify-content / align-content:控制 整个网格在容器里的位置

当整个网格没有撑满容器时,justify-contentalign-content 才明显。

例如:

1
2
3
4
5
6
7
8
9
.container {
display: grid;
width: 1000px;
height: 500px;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(2, 100px);
justify-content: center;
align-content: center;
}

这里网格本身只有 300×200,所以会整体在容器中居中。


13. 页面布局实战:头部 + 侧边栏 + 主内容 + 底部

这是 Grid 最经典的应用。

HTML

1
2
3
4
5
6
<div class="layout">
<header class="header">头部</header>
<aside class="sidebar">侧边栏</aside>
<main class="main">主内容</main>
<footer class="footer">底部</footer>
</div>

CSS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.layout {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-rows: 60px 1fr 50px;
gap: 12px;
min-height: 100vh;
}

.header {
grid-column: 1 / 3;
}

.sidebar {
grid-column: 1 / 2;
}

.main {
grid-column: 2 / 3;
}

.footer {
grid-column: 1 / 3;
}

这个布局的意思:

  • 两列:左侧固定 240px,右侧自适应
  • 三行:头部 60px,中间撑满,底部 50px
  • 头部和底部都横跨两列

这是最基础的后台布局写法。


14. grid-template-areas:可读性更强

如果你不想写一堆 grid-column,可以用区域命名。

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
.layout {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-rows: 60px 1fr 50px;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
gap: 12px;
min-height: 100vh;
}

.header {
grid-area: header;
}

.sidebar {
grid-area: sidebar;
}

.main {
grid-area: main;
}

.footer {
grid-area: footer;
}

这个写法的优点是:

  • 一眼就能看出页面结构
  • 维护成本低
  • 很适合复杂页面骨架

grid-template/grid-template-areas 也是 Grid 模板定义的重要部分。


15. 响应式怎么做

Grid 很适合响应式,但别指望一套代码打天下。

最常见做法:

方法 1:auto-fit + minmax

1
2
3
4
5
.list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 16px;
}

适合卡片流。


方法 2:媒体查询切列数

1
2
3
4
5
6
7
8
9
10
11
.layout {
display: grid;
grid-template-columns: 240px 1fr;
gap: 16px;
}

@media (max-width: 768px) {
.layout {
grid-template-columns: 1fr;
}
}

小屏时把双栏改成单栏。


方法 3:配合 grid-template-areas

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.layout {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}

@media (max-width: 768px) {
.layout {
grid-template-columns: 1fr;
grid-template-areas:
"header"
"sidebar"
"main"
"footer";
}
}

这个对于复杂页面很实用。


17. Grid 常见坑

17.1 忘了父元素必须是 display: grid

你给子元素写:

1
grid-column: 1 / 3;

但父元素不是 grid container,完全没用。


17.2 只对直接子元素生效

Grid 只管 父元素的直接子元素

1
2
3
4
5
<div class="container">
<div class="wrapper">
<div class="item"></div>
</div>
</div>

这里 .item 不是 .container 的直接子元素,所以 .container 的 Grid 不会直接摆 .item


17.3 行高没定义时,表现和你想的不一样

如果你只定义列,不定义行:

1
grid-template-columns: repeat(3, 1fr);

那行高通常会由内容撑开。

这不是错,是正常行为。

如果你想统一行高,要写:

1
grid-auto-rows: 120px;

或者:

1
grid-template-rows: repeat(2, 120px);

17.4 1fr 不是“永远平均”

它是“分配剩余空间”,不是“绝对平均”。

如果你前面有固定列:

1
grid-template-columns: 200px 1fr 1fr;

那是先扣掉 200px,再把剩余空间平分。


17.5 内容过长会把布局挤坏

有时某个单元格里是长字符串、长代码、超长链接,会把列撑爆。

常见处理:

1
2
3
4
.item {
min-width: 0;
overflow: hidden;
}

这个在实际项目里很常见。


18. auto-fillauto-fit 的区别

auto-fill / auto-fit 只能用在 repeat() 里 它们本质不是“值”,而是:一种“列数量生成策略”

1
2
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));

两者都能自动生成列。

区别在于:

  • auto-fill:尽量“保留空轨道”
  • auto-fit:会让实际有内容的列尽量撑开

18.1 核心差异

fill:先算列数 → 所有列都参与分配(包括空列)

fit:先算列数 → 空列删除 → 剩下的列参与分配

你可以这样理解(更准确):

  • auto-fill
1
2
3
1. 计算最多能放多少列
2. 创建这些列(哪怕没内容)
3. 所有列一起分空间
  • auto-fit
1
2
3
4
1. 计算最多能放多少列
2. 创建这些列
3. 删除空列
4. 剩下的列分空间

fill:先分列数,然后所有列(包括空列)均分空间

fit:先分列数,去掉空列,然后剩下的列均分空间

fill:空列也参与分配
fit:空列不参与分配


18.2 很关键的点

👉 “均分”只有在 max 是 1fr 时才成立

如果是:

1
minmax(200px, 300px)

那就不是“均分”了,而是:

最多分到 300px,然后停住

18.3 工程翻译 辅助记忆

  1. auto-fill
    自动填满轨道(哪怕是空的)
    👉 关键词:
    “填满 + 保留空位”

  2. auto-fit
    自动贴合内容(删除空轨道)
    👉 关键词:
    “贴合 + 去空位”

  3. auto-fill
    先画满格子 → 再往里放元素
    👉 格子是主角
    👉 元素只是填进去

  4. auto-fit
    先画格子 → 把空格子删掉 → 剩下的拉伸
    👉 元素是主角
    👉 格子为元素服务

19. minmax() 很重要

minmax(min, max) 表示轨道尺寸最小值和最大值。

例如:

1
grid-template-columns: repeat(3, minmax(150px, 1fr));

意思是:

  • 每列最小 150px
  • 最大可拉伸到 1fr

这能避免列太窄,也能保证大屏下能扩展。

20. subgrid 是什么

subgrid 用在嵌套网格里。它允许子网格继承父网格的行或列轨道。这个能力已广泛可用。 ([MDN Web Docs][8])

简单理解:

你有一个大 Grid,里面每个卡片又是个 Grid。如果你想让内部布局和外部列线严格对齐,subgrid 很有用。