CSS布局(六):Grid —— 像围棋一样布局

第六章:Grid------二维布局的终极方案

上一章我们学习了 Flex,它擅长处理一维布局(要么排成一行,要么排成一列)。但现实中的页面往往是二维的------既有行,又有列。比如:

  • 图片墙(多行多列)
  • 复杂后台仪表盘
  • 杂志风格的卡片布局
  • 整体页面框架(侧边栏 + 主内容 + 多个区域)

这些需求,Flex 能做,但往往要嵌套多层,代码变得复杂。而 Grid 布局 ,就是为了直接处理二维网格而生的。


6.1 Flex 的局限:为什么还需要 Grid?

flex-wrap: wrap 确实可以让 Flex 项目换行,产生多行的效果,看起来像二维。

那为什么官方和教程还坚持说 Flex 是 一维 ,Grid 才是 二维 呢?

核心区别在于:你是如何控制"换行后那些行"的。

1. Flex 的"换行"是无奈之举,它不关心"行"的关系

当 Flex 换行后,每一行都是一个独立的、新的 Flex 容器。行与行之间几乎没有关系。

  • 你看不到列 :第一行的第一个元素,和第二行的第一个元素,在垂直方向上没有任何对齐关系。它们只是各自被自己的行居中、拉伸或排列。
  • 你无法控制"行"的整体结构:你不能说"让所有行的高度比例是 1:2:1",也不能说"让第二行的元素整体向下移动10px"。

打个比方:Flex 换行就像一排人并排站,站不下了就自然到后面再起一排。 后面那排人不会刻意对齐前面那排人的位置,每排都是独立重新排列。

2. Grid 的"二维"是主动设计,它同时规划了行和列

Grid 在声明时,就同时画好了行轨道列轨道,形成一个真正的"表格"结构。

  • 列是真实存在的:所有行里的元素,都会严格对齐到你定义的那些列线上。
  • 你可以整体控制:你可以说"所有行的行高都是1fr"、"第二行的高度是100px"、"让第一个元素横跨两行两列"。

打个比方:Grid 就像在一张画好格子的棋盘上下棋。 每个格子都有固定的坐标(第几行,第几列),你可以把棋子放在任何一个格子里,或者让一个棋子占多个格子。

用代码直观对比:实现一个"照片墙"

假设我们有6个方块,希望它们排成2行3列,每个格子大小一致。

用 Flex (换行) 实现:

css 复制代码
.flex-container {
  display: flex;
  flex-wrap: wrap;
}
.flex-item {
  width: 33.333%; /* 必须手动算宽度,强迫成3列 */
  /* 或者用 flex: 1 0 33.333% */
}

问题来了:

  • 你想让第二行的三个方块,在垂直方向上和第一行严格对齐吗?它们本来就在同一列上,但因为Flex没有"列"的概念,其实是靠宽度硬算出来的,容易因边框、边距产生偏差。
  • 你想让第一行的高度是100px,第二行是200px?Flex做不到。 它会根据内容撑开,你无法单独控制某一行的行高。

用 Grid 实现:

css 复制代码
.grid-container {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr; /* 明确3列 */
  grid-auto-rows: minmax(100px, auto); /* 行高默认自动 */
}
/* 想让第一行高100px,第二行高200px? */
.grid-container {
  grid-template-rows: 100px 200px; /* 直接声明! */
}

清晰明了: 列是列,行是行,你可以随意操控整个网格结构。

一张表格彻底说清楚
特性 Flexbox (一维) Grid (二维)
控制重心 控制 项目本身 在主轴上的分布、对齐、顺序 控制 容器结构,先画好行和列的轨道
换行后的关系 换行后,行与行独立,无法对齐不同行的项目 行和列从一开始就定义好了,所有项目按坐标对齐
能否单独控制某一行的高度? 不能 。所有行的高度由内容决定,你只能统一设置 align-content 。用 grid-template-rows 精确指定每一行的高度
能否让一个元素同时跨越多行多列? 不能。Flex 项目永远是一条直线上的一个点 grid-row: span 2; grid-column: span 2
最适用场景 导航栏、列表项、表单内元素对齐(内容数量不确定 整体页面布局、复杂卡片墙、仪表盘(结构确定
总结:一句话区分
  • Flex 的一维 :它只关心一条线 上的排列。即使换行产生了多条线,它也不认为这些线组成了一个平面,每条线依然各行其是。
  • Grid 的二维 :它一上来就声明这是一个平面。先有行和列的网格框架,再把项目放进去。

所以,正确的理解是:

Flex 可以模拟 出二维的效果(通过换行和固定宽度),但它缺乏控制二维结构的能力(对齐行与行、控制特定行高、元素跨行跨列)。而 Grid 天生就为二维设计,这些能力是内置的。

学完 Flex 再学 Grid,你最大的感受就是:"以前用 Flex 绞尽脑汁凑出来的复杂布局,用 Grid 简直是降维打击。"


6.2 核心概念详解:Grid 网格布局 ------ 像棋盘、像表格、像渔网

整体画面 :想象你在下一盘围棋 / 做一个Excel表格 。

横线和竖线交织成一张"网",网上的每一个小"格子"就是一个独立的位置。

你可以让一个东西只占一个格子,也可以让它占连续的好几个格子。


一、为什么叫"网格"(Grid)?

因为它的样子就像一张"网"+"格"

  • 渔网:有横的绳子、竖的绳子,交织在一起
  • Excel表格:横线竖线切割出来的表格

"网" 指的是横竖交错的线(网格线)
"格" 指的是线围出来的一个个小空间(单元格)

所以"网格"="网状的格子结构"。你脑海里应该浮现的是:一张由横线和竖线编织成的网,网上全是整齐的小格子


二、网格线(grid lines)------ 棋盘上的"坐标线"

画面感:就像一张坐标纸,有横着的线和竖着的线,线就是边界和编号。

网格线是 Grid 的"骨架"。一个 3×3 的网格,有 4 条纵向网格线(列线)和 4 条横向网格线(行线)。

复制代码
竖线编号:1    2    3    4
         |    |    |    |
横线1 ------ +------------+------------+------------+
         |    |    |    |
横线2 ------ +------------+------------+------------+
         |    |    |    |
横线3 ------ +------------+------------+------------+
         |    |    |    |
横线4 ------ +------------+------------+------------+

项目可以指定从哪条线开始,到哪条线结束。

通俗理解 :你可以告诉一个元素"从第2条竖线开始,到第4条竖线结束",它就知道自己该占多宽了。就像告诉一个人"从A街道走到C街道"

为什么叫"线"?

因为就是字面意思------画出来的线。在真实世界里:

  • 你在纸上画表格,是不是先画横线竖线
  • 围棋棋盘上,是不是有横线和竖线
  • 建筑图纸上,是不是用线来划分区域?

Grid里的"线"就是你划分布局时看不见但真实存在的边界。第1条线、第2条线、第3条线......就像尺子上的刻度线。

为什么不是"边界"而是"线"?

因为"边界"强调的是"边缘",而"线"强调的是位置编号------你可以说"从第2条线到第4条线",就像说"从2楼到4楼"。


三、为什么叫"行"(row)和"列"(column)?

这是从现实世界借来的概念

场景
Excel表格 横着的一排(1行、2行) 竖着的一排(A列、B列)
电影院 第1排、第2排(横着坐一排人) 第1列、第2列(竖着的一串座位)
教室座位 第1行同学(左右一排) 第1列同学(上下一条)
报纸排版 一行文字(从左到右) 一列文字(从上到下)

记忆口诀
行 = 横行 = 横着走(左右方向)
列 = 队列 = 竖着排队(上下方向)

在Grid里完全沿用这个现实世界的叫法,因为所有人类都已经理解什么是行和列了。


四、网格轨道(grid tracks)------ 一整条"行通道"或"列通道"

画面感 :不是单个格子,而是一整排格子连起来的那条"通道"

就是"行"或"列"。定义网格时,你用 grid-template-columnsgrid-template-rows 来指定每条轨道的大小。

复制代码
第1行轨道 ------------------------------------------------
第2行轨道 ------------------------------------------------
第3行轨道 ------------------------------------------------

通俗理解:轨道就是"车道"。每一行就像一条横向车道,每一列就像一条纵向车道。你告诉系统"车道有多宽",所有车(子元素)就沿着它走。

为什么叫"轨道"(track)?

这个比喻来自"火车轨道"或"田径跑道"

想象一个田径跑道:

  • 第1跑道、第2跑道、第3跑道......
  • 每条跑道是一整条从头到尾的通道
  • 跑道上的运动员(子元素)在这条通道里移动

轨道 ≠ 单元格

  • 单元格是"一个格子"(一个小方块)
  • 轨道是"一整行"或"一整列"(一排格子连起来的整条通道)

为什么不用"行/列"而另造一个"轨道"?

因为"行/列"指的是位置 ,而"轨道"强调的是尺寸和空间

  • grid-template-rows: 100px 200px → 第1条轨道高100px,第2条轨道高200px
  • 你是在定义每条"通道"的宽度/高度,而不是在数第几行

画面感:你站在火车站台上,面前是好几条平行的铁轨(轨道),每条轨道可以跑一列火车(一排子元素)。


五、网格单元格(grid cell)------ 最小的"格子"

画面感 :Excel表格里的一个格子

一个行轨道和一个列轨道的交集。项目默认占据一个单元格。

复制代码
   列1   列2   列3
行1 [ A ] [ B ] [ C ]
行2 [ D ] [ E ] [ F ]
  • A占据的就是第1行第1列这个单元格
  • 默认情况下,一个项目只占1个单元格

通俗理解:就像教室的座位表,一个学生默认只坐一个座位。

为什么叫"单元格"(cell)?

从生物学和表格里借来的词

领域 单元格的意思
Excel 行和列交叉的那个小方框
表格 <td> 叫"table data cell"
生物学 细胞是最小单位(cell)
监狱 一间间小牢房(cell)

Grid里的单元格 = 一条行轨道和一条列轨道交叉 出来的最小独立单位

为什么不是"格子"而是"单元格"?

因为"格子"太口语化,而"单元格"强调了它是可以独立存在的最小单元------就像生物体的细胞,不能再拆了。

画面感:你在Excel里看到一个表格,最小的那个小方块就是单元格。一个项目默认就占一个单元格。


六、网格区域(grid area)------ 合并出来的"大块区域"

画面感 :合并多个相邻单元格,形成一个矩形大格子

由多个单元格组成的矩形区域。可以让一个项目跨越多行多列。

你可以让一个元素:

  • 占第1行到第3行
  • 占第2列到第4列

这样它就占了一个 3行 × 3列 的大矩形区域。

复制代码
┌─────┬─────┬─────┐
│     │     │     │
│  大  │  B  │  C  │
│  区  │     │     │
│  域  ├─────┼─────┤
│     │  D  │  E  │
└─────┴─────┴─────┘

通俗理解:就像把几个小课桌拼成一个大桌子,几个同学一起用。

为什么叫"区域"(area)?

因为就是字面意思------一块"区域"

当多个单元格合并在一起,就形成了一块连续的矩形区域

区域 vs 单元格

  • 单元格 = 最小单位(1×1)
  • 区域 = 多个单元格拼成的大块(比如 2行×3列)

为什么叫"区域"不叫"合并单元格"?

因为"合并单元格"强调的是操作过程 (把几个格子并起来),而"区域"强调的是结果------这块地方现在是一个整体了。

画面感 :你在Excel里选中一片连续的格子,它们形成一个蓝色高亮的矩形区域。你可以给这个区域起个名字叫"header",然后直接把一个元素扔进去。


Grid 完整记忆链条(由小到大 + 为什么这么叫)
名词 核心比喻 一句话理解 为什么这么叫
网格 渔网 + 格子 横竖线交织成的网状结构 像渔网的"网"+ 格子的"格"
线 尺子的刻度 划分位置的边界线,可以数编号 画表格先画线,线就是边界和编号
电影院排 横着的一排(左右方向) 现实世界叫法,"横行"
竖着的队列 竖着的一列(上下方向) 现实世界叫法,"队列"
轨道 火车轨道 / 跑道 一整条通道,用来定义尺寸 像火车轨道,强调尺寸和空间
单元格 Excel格子 / 细胞 行与列交叉的最小单位 像生物细胞,是最小独立单位
区域 选中的连续矩形 多个单元格拼成的大块 字面意思,一块连续的"区域"

6.3 Grid 核心属性(一):容器上的属性

6.3.1 display: gridinline-grid
css 复制代码
.container {
  display: grid;        /* 块级网格容器,占满整行宽度 */
}
.container-inline {
  display: inline-grid; /* 行内网格容器,宽度由内容撑开 */
}

6.3.2 定义网格结构:grid-template-columnsgrid-template-rows

你已经知道了:

  • 网格线 = 棋盘上的坐标线
  • = 横着的一排(横行)
  • = 竖着的一排(队列/柱子)
  • 轨道 = 一整条通道(火车轨道/跑道)
  • 单元格 = 行与列交叉的最小格子
  • 区域 = 多个单元格合并成的大块

现在我们要学的是:如何定义网格有多少行、多少列,以及每一行多高、每一列多宽。

这就是 grid-template-columnsgrid-template-rows 要做的事。

先拆开词本身:grid-template-columns
  1. grid ------ 网格:就是那张横竖交织的"网+格"。

  2. template ------ 模板 / 样板

    为什么叫 template? 想象你盖房子:你先画一张设计图纸 (template),图纸上写着:"第1面墙多厚,第2面墙多厚......"工人按照这张图纸去施工。
    template 在 Grid 里 = 你提前设定好的"结构蓝图",你告诉浏览器:"按照我这个模板来划分行和列。"

    画面感 :就像做月饼的模具 ------模具长什么样,月饼就长什么样。grid-template-columns 就是"列方向的模具"。

  3. columns ------ 列(复数):前面讲过列 = 竖着的一排(像一列火车、一列队伍)。

    为什么是 column 不是 row?

    英文 中文 方向 记忆
    column 竖 ↕️ 像一根柱子(column 本意就是柱子)
    row 横 ↔️ 像一排划船(row 本意是划船,船桨左右划)
  4. 合起来:grid-template-columns

    中文直译 :网格的列方向模板

    "我要告诉浏览器,我的网格里每一列有多宽,按什么顺序排列。"

    画面感:你拿着一个尺子,在纸上画表格:

    • 第1列:200px 宽
    • 第2列:1fr(剩下的空间里占1份)
    • 第3列:200px 宽

    grid-template-columns: 200px 1fr 200px; 就是在说这句话。

同理:grid-template-rows
  1. rows ------ 行(复数)

    为什么是 row? Row 的本意是"划船"------船桨左右划动,形成横向的排列
    记忆 :电影院里的 row = 一排座位(横着的)

  2. 合起来:grid-template-rows

    中文直译 :网格的行方向模板

    "我要告诉浏览器,我的网格里每一行有多高。"

    画面感:还是那个表格:

    • 第1行:100px 高
    • 第2行:auto(内容多高就多高)
    • 第3行:100px 高

    grid-template-rows: 100px auto 100px; 就是在说这句话。

完整代码示例
css 复制代码
.container {
  display: grid;
  grid-template-columns: 200px 1fr 200px;   /* 三列:固定200px + 自适应 + 固定200px */
  grid-template-rows: 100px auto 100px;     /* 三行:固定100px + 自适应 + 固定100px */
}

这个网格,列方向 :第一列固定200px,第二列占满剩下的空间,第三列固定200px。
行方向:第一行固定100px高,第二行随内容自动撑高,第三行固定100px高。

视觉画面

复制代码
┌─────────┬──────────────────┬─────────┐
│         │                  │         │
│ 200px   │      1fr          │  200px  │
│ 固定宽  │    (自适应宽)    │  固定宽  │
│         │                  │         │
├─────────┼──────────────────┼─────────┤
│         │                  │         │
│   auto  │      auto         │   auto  │
│ 内容多高│    内容多高        │ 内容多高│
│ 就多高  │    就多高          │ 就多高  │
│         │                  │         │
├─────────┼──────────────────┼─────────┤
│         │                  │         │
│ 100px   │      100px        │  100px  │
│ 固定高  │     固定高         │ 固定高  │
│         │                  │         │
└─────────┴──────────────────┴─────────┘
单位详解(每个都讲透)

1. px ------ 像素(pixel)

全称 中文 是什么
pixel 像素 屏幕上的一个发光点

为什么叫 pixel? pix 来自 pictures(图片),el 来自 element(元素)→ 图片的最小元素

画面感 :你凑近屏幕,看到一个个小光点,每个光点就是一个 px
在 Grid 里200px = 固定占200个小光点的宽度,雷打不动

2. % ------ 百分比(percent)

全称 中文 是什么
percent 百分比 per(每)+ cent(百)= 每一百份中的多少

画面感 :你把容器宽度切成100份,25% 就是拿其中25份。

注意% 是相对于父容器的宽度/高度,不是相对于剩余空间。

示例

css 复制代码
.container {
  width: 1000px;
  grid-template-columns: 25% 75%;
}
/* 第一列 = 250px,第二列 = 750px */

3. fr ------ 分数(fraction)【Grid 最核心的单位】

全称 中文 是什么
fraction 分数 / 部分 表示"分一份"

为什么叫 fr? fr 就是 fraction 的缩写。Fraction 在数学里是"分数",在日常英语里是"一小部分"。

画面感:你和朋友分一个披萨:

  • 先拿走固定的几块(px% 列)
  • 剩下的披萨,按比例分
css 复制代码
grid-template-columns: 200px 1fr 2fr;
  • 第1列:先拿走200px
  • 剩下的空间分成 3份(1+2)
  • 第2列占 1份
  • 第3列占 2份

为什么 fr 这么重要? 因为以前用 %px 很难做"一个固定,剩下的自适应"。fr 就是专门解决这个问题的。

4. auto ------ 自动

全称 中文 是什么
auto 自动 由内容决定

画面感:你放了一个很长的单词进去,这一列就会变宽到能装下它。你放了一个空 div,这一列就会缩到最小。

auto vs 1fr 的区别

特性 auto 1fr
如何决定大小 内容决定 剩余空间决定
会不会被内容撑开 会,内容多宽它就多宽 会,但会按比例分配
最小宽度 通常是内容的最小宽度(比如一个单词) 可以被压缩到0(如果内容允许换行)

示例

css 复制代码
grid-template-columns: auto 1fr;
  • 第一列:紧贴内容的宽度
  • 第二列:占满剩下的所有空间

5. minmax(min, max) ------ 最小最大值

全称 中文 是什么
min + max 最小 + 最大 一个范围

为什么叫 minmax? 就是 min(最小)和 max(最大)拼在一起。告诉你:"这一列的宽度,在这个范围内变化"。

画面感 :像一个橡皮筋

  • 最窄不能短于 min
  • 最宽不能超过 max
  • 中间可以自由伸缩

示例

css 复制代码
grid-template-columns: minmax(100px, 1fr) 2fr;
  • 第一列:最小 100px,最大占 1 份剩余空间
  • 当容器变窄时,第一列会先被压缩到 100px,然后不再缩小
  • 第二列继续被压缩

为什么需要 minmax? 因为 1fr 理论上可以被压缩到 0,但如果你不想让某一列太窄(比如侧边栏不能小于 200px),就用 minmax(200px, 1fr)

6. repeat(次数, 值) ------ 重复

全称 中文 是什么
repeat 重复 写一次,重复多次

为什么叫 repeat? 就是英文"重复"的意思。不想写 1fr 1fr 1fr 1fr 这么长,就写 repeat(4, 1fr)

画面感:就像复印机------你告诉它"复印4份",它就帮你复制4遍。

示例

css 复制代码
/* 这两种写法完全等价 */
grid-template-columns: 1fr 1fr 1fr;
grid-template-columns: repeat(3, 1fr);

/* 复杂一点:重复一个模式 */
grid-template-columns: repeat(2, 100px 1fr);
/* 展开 = 100px 1fr 100px 1fr */
中英文 + 概念速查表
英文 中文 怎么记
grid 网格 渔网 + 格子
template 模板 / 蓝图 做月饼的模具
columns C 是竖半圆 = 竖,柱子
rows 电影院一排座位 = 横
px 像素 屏幕上的一个光点
% 百分比 每100份里拿几份
fr 分数 / 份 fraction = 分肉,分剩余空间
auto 自动 内容多宽/多高就多宽/多高
minmax() 最小最大 橡皮筋,有下限有上限
repeat() 重复 复印机,复制多份
串起所有内容

你要画一张网格 (grid)。

先拿出一张模板 (template)图纸。

在图纸上写:

  • 列方向(columns):第一列 200px(固定),第二列 1fr(剩下的分一份),第三列 200px(固定)
  • 行方向(rows):第一行 100px,第二行 auto(随内容),第三行 100px

如果有一列你想让它最小 100px,最大 1fr ,就用 minmax(100px, 1fr)

如果你想写 3 个 1fr 又懒得敲三遍,就用 repeat(3, 1fr)


6.3.3 网格间距:gap
css 复制代码
.container {
  gap: 20px;           /* 行间距和列间距都是 20px */
  gap: 20px 10px;      /* 行间距 20px,列间距 10px */
  row-gap: 20px;       /* 单独设置行间距 */
  column-gap: 10px;    /* 单独设置列间距 */
}

注意:gap 不会在网格边缘产生间距,只出现在格子之间。


6.3.4 单元格内对齐:justify-itemsalign-items
  • justify-items :水平方向对齐(每个单元格内)

    控制单元格内的项目 在水平方向上的对齐方式(相当于 Grid 的 align-items 的水平版本)。

    | 值 | 含义 |

    |---|---|---|

    | stretch(默认) | 拉伸填满整个单元格宽度 |

    | start | 向单元格左侧对齐 |

    | end | 向单元格右侧对齐 |

    | center | 单元格内水平居中 |

  • align-items :垂直方向对齐(每个单元格内)

    控制单元格内的项目在垂直方向上的对齐方式。

    | 值 | 含义 |

    |---|---|---|

    | stretch(默认) | 拉伸填满整个单元格高度 |

    | start | 向单元格顶部对齐 |

    | end | 向单元格底部对齐 |

    | center | 单元格内垂直居中 |

6.3.5 网格整体对齐:justify-contentalign-content
  • justify-content :整个网格在容器中的水平对齐

    当网格的总宽度小于容器宽度时,控制网格整体在容器中的位置。

    | 值 | 含义 |

    |---|---|---|

    | start(默认) | 网格整体靠左 |

    | end | 网格整体靠右 |

    | center | 网格整体居中 |

    | space-between | 网格两端对齐,列轨道之间间隔相等 |

    | space-around | 每个列轨道两侧间隔相等 |

    | space-evenly | 列轨道之间及两端间隔全相等 |

  • align-content :整个网格在容器中的垂直对齐

    当网格的总高度小于容器高度时,控制网格整体在容器中的位置。取值同上。

6.3.6 隐式轨道尺寸:grid-auto-rowsgrid-auto-columns

当项目被放置到没有明确定义的行或列时,使用这些属性决定隐式轨道的大小。

css 复制代码
.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);  /* 只定义了 3 列 */
  grid-auto-rows: 100px;                   /* 自动生成的行高度为 100px */
}

如果项目被放到第 4 列(超出定义),grid-auto-columns 会决定那一列的宽度。


6.4 Grid 核心属性(二):项目上的属性

这些属性写在子项目上,控制单个项目在网格中的位置。

1. grid-columngrid-row

指定项目从哪条网格线开始,到哪条网格线结束。

css 复制代码
.item {
  grid-column: 1 / 3;   /* 从列线1开始,到列线3结束(占2列) */
  grid-row: 2 / 4;      /* 从行线2开始,到行线4结束(占2行) */
}

简写与等价写法

写法 含义
grid-column: 1 / 3 从第 1 条列线到第 3 条列线
grid-column: 1 / span 2 从第 1 条列线开始,跨越 2 列
grid-column: span 2 / 3 跨越 2 列,结束于第 3 条列线
grid-column: 2 等价于 2 / 3(只占第 2 列)

组合写法

css 复制代码
.item {
  grid-column: 1 / span 2;  /* 从列线1开始,占2列 */
  grid-row: 2 / 4;          /* 从行线2开始,到行线4结束 */
}
2. grid-area:两种用法

用法一:通过网格线指定区域

css 复制代码
.item {
  grid-area: 2 / 1 / 4 / 3;  /* 行开始 / 列开始 / 行结束 / 列结束 */
}
/* 等价于:grid-row: 2 / 4; grid-column: 1 / 3; */

用法二:通过命名区域

配合容器的 grid-template-areas 使用:

css 复制代码
.container {
  display: grid;
  grid-template-areas:
    "header header header"
    "sidebar main main"
    "footer footer footer";
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }

这种方法最直观:用 ASCII 画图的方式定义布局。

3. justify-self:覆盖容器的 justify-items

控制单个项目在单元格内的水平对齐,取值同 justify-items

4. align-self:覆盖容器的 align-items

控制单个项目在单元格内的垂直对齐,取值同 align-items


6.5 隐式网格 vs 显式网格

这是一个容易混淆但很重要的概念。

显式网格 :你用 grid-template-rows/columns 明确定义的行和列。

隐式网格:当你把项目放到显式网格之外时,浏览器自动创建的行和列。

css 复制代码
.container {
  display: grid;
  grid-template-columns: 100px 100px;   /* 显式:只定义了2列 */
  grid-template-rows: 100px;            /* 显式:只定义了1行 */
  grid-auto-rows: 50px;                 /* 隐式行的高度 */
}
html 复制代码
<div class="container">
  <div class="item">1</div>   <!-- 放到 (行1, 列1) 显式区域 -->
  <div class="item">2</div>   <!-- 放到 (行1, 列2) 显式区域 -->
  <div class="item">3</div>   <!-- 放到 (行2, 列1) → 自动创建第2行,高度50px -->
  <div class="item" style="grid-column: 4;">4</div>  <!-- 放到第4列 → 自动创建第3、4列 -->
</div>

关键点

  • 隐式轨道的尺寸由 grid-auto-rowsgrid-auto-columns 控制
  • 如果没有设置,默认为 auto(由内容决定)
  • grid-auto-flow 控制隐式网格的排列方向(rowcolumn

6.6 实战案例:从零搭建常见布局

案例一:经典三栏布局(页眉 + 侧边栏 + 主内容 + 页脚)
html 复制代码
<div class="layout">
  <header>页眉</header>
  <aside>侧边栏</aside>
  <main>主内容区域</main>
  <footer>页脚</footer>
</div>
css 复制代码
.layout {
  display: grid;
  grid-template-areas:
    "header header"
    "sidebar main"
    "footer footer";
  grid-template-columns: 250px 1fr;
  grid-template-rows: auto 1fr auto;
  min-height: 100vh;
}
header { grid-area: header; background: #333; color: white; padding: 1rem; }
aside { grid-area: sidebar; background: #f0f0f0; padding: 1rem; }
main { grid-area: main; background: white; padding: 1rem; }
footer { grid-area: footer; background: #333; color: white; padding: 1rem; }

效果 :侧边栏固定 250px,主内容自适应,页脚始终在底部(因为 1fr 把剩余高度分配给 main)。

案例二:图片墙(响应式,自动填充)
html 复制代码
<div class="gallery">
  <img src="1.jpg" alt=""><img src="2.jpg" alt="">
  <img src="3.jpg" alt=""><img src="4.jpg" alt="">
  <!-- 任意多张图片 -->
</div>
css 复制代码
.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 16px;
}
.gallery img {
  width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: cover;
}

auto-fillminmax() 的组合:浏览器自动计算能放下多少列,每列最小 200px,最大占 1 份剩余空间。窗口缩放时,列数自动变化。

案例三:杂志风格卡片布局(不规则网格)
html 复制代码
<div class="magazine">
  <div class="feature">特色文章</div>
  <div class="card">卡片1</div>
  <div class="card">卡片2</div>
  <div class="card">卡片3</div>
  <div class="card">卡片4</div>
  <div class="card">卡片5</div>
</div>
css 复制代码
.magazine {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 20px;
}
.feature {
  grid-column: span 2;   /* 特色文章占2列 */
  grid-row: span 2;      /* 特色文章占2行 */
  background: #f8f9fa;
  padding: 2rem;
}
.card {
  background: white;
  border: 1px solid #ddd;
  padding: 1rem;
}

效果:第一篇文章是"大卡片"占 2×2 格子,其余卡片正常排列。Grid 会自动调整其他项目的位置来填补空隙。

案例四:仪表盘(复杂区域划分)
html 复制代码
<div class="dashboard">
  <div class="header">仪表盘标题</div>
  <div class="sidebar">菜单</div>
  <div class="stats">统计图表</div>
  <div class="recent">最近活动</div>
  <div class="trends">趋势图</div>
  <div class="footer">版权信息</div>
</div>
css 复制代码
.dashboard {
  display: grid;
  grid-template-areas:
    "header header header"
    "sidebar stats trends"
    "sidebar recent recent"
    "footer footer footer";
  grid-template-columns: 250px 1fr 1fr;
  grid-template-rows: auto 300px auto auto;
  gap: 16px;
  min-height: 100vh;
}
.header { grid-area: header; background: #2c3e50; color: white; padding: 1rem; }
.sidebar { grid-area: sidebar; background: #34495e; color: white; padding: 1rem; }
.stats { grid-area: stats; background: #ecf0f1; padding: 1rem; }
.recent { grid-area: recent; background: #ecf0f1; padding: 1rem; }
.trends { grid-area: trends; background: #ecf0f1; padding: 1rem; }
.footer { grid-area: footer; background: #2c3e50; color: white; padding: 1rem; }
案例五:响应式网格(移动端一列,平板两列,桌面四列)
css 复制代码
.grid {
  display: grid;
  gap: 20px;
  grid-template-columns: 1fr;  /* 移动端默认:1列 */
}

@media (min-width: 600px) {
  .grid {
    grid-template-columns: repeat(2, 1fr);  /* 平板:2列 */
  }
}

@media (min-width: 1000px) {
  .grid {
    grid-template-columns: repeat(4, 1fr);  /* 桌面:4列 */
  }
}

或者更简洁地用 auto-fill + minmax() 实现纯响应式:

css 复制代码
.grid {
  display: grid;
  gap: 20px;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
/* 浏览器自动根据容器宽度决定列数,无需 media query */

auto-fitauto-fill 的区别:

  • auto-fill:尽可能多地填充列,即使列是空的也保留轨道
  • auto-fit:填充后,把空轨道的空间分配给非空轨道

6.7 Grid 与 Flex 的配合使用

Grid 和 Flex 不是二选一,而是黄金搭档

原则

  • Grid 搭建页面的宏观结构(整体框架、大区块)
  • Flex 处理微观排列(导航栏内的链接、卡片内的元素)
html 复制代码
<div class="page">
  <header class="header">
    <div class="logo">Logo</div>
    <nav class="nav">   <!-- 这里用 Flex -->
      <a href="#">首页</a>
      <a href="#">产品</a>
      <a href="#">关于</a>
    </nav>
  </header>
  <main class="main">   <!-- Grid 布局 -->
    <aside>侧边栏</aside>
    <article>文章</article>
  </main>
  <footer>页脚</footer>
</div>
css 复制代码
.page {
  display: grid;
  grid-template-rows: auto 1fr auto;
  min-height: 100vh;
}
.header {
  display: flex;
  justify-content: space-between;  /* Flex 处理水平排列 */
  align-items: center;
}
.nav {
  display: flex;
  gap: 20px;                      /* Flex 处理链接间距 */
}
.main {
  display: grid;
  grid-template-columns: 250px 1fr;
}

经验法则:父容器定大局用 Grid,子元素内部排小队用 Flex。


6.8 Grid vs Flex:何时用哪个?

场景 推荐方案 原因
导航栏(一排链接) Flex 一维排列,简单直接
整体页面框架(页眉/侧边栏/主内容/页脚) Grid 二维结构,行列对齐
卡片列表(自动换行) 两者皆可 Flex-wrap 或 Grid auto-fill
图片墙(多行多列,行列对齐) Grid 天然网格,行列可控
表单内的标签+输入框 Flex 简单行内对齐
仪表盘、杂志布局 Grid 复杂区域跨行跨列
垂直居中 两者皆可 Flex 更简洁
等分布局(无跨行跨列需求) 两者皆可 Flex:1 或 Grid:1fr

一个简单的判断流程

  1. 问自己:这个布局是一维 (只有行或只有列)还是二维(行和列都要控制)?
  2. 如果是一维 → 用 Flex
  3. 如果是二维 → 用 Grid
  4. 如果不确定 → 先试 Grid,因为 Grid 可以做任何 Flex 能做的事(只是代码可能略长)

本章小结

  • Grid 容器 = 二维网格系统
  • 核心概念:网格线、网格轨道、网格单元格、网格区域
  • 关键单位fr(剩余空间分数)、minmax()repeat()
  • 显式网格 :用 grid-template-* 定义
  • 隐式网格 :自动生成,用 grid-auto-* 控制
  • 项目定位grid-columngrid-rowgrid-area(支持命名区域)
  • Grid 与 Flex 是互补关系:Grid 管大局,Flex 管细节
相关推荐
谢尔登1 小时前
【Next】客户端组件和服务端组件
前端·javascript·react.js·架构
Mintopia1 小时前
合合信息蜜蜂 AI 最新资讯(2026.4.22 官方发布)
前端
Mintopia1 小时前
如何用第一性原理提升问题解决能力
前端
禅思院2 小时前
下篇:打造可观测的异步加载防御体系
前端·架构·前端框架
|晴 天|2 小时前
Vue 3 项目错误处理实战:Vue ErrorHandler、Promise 监控、用户友好提示
前端·javascript·vue.js
Cobyte2 小时前
8.响应式系统比对:手写 SolidJS 响应式系统
前端·javascript·vue.js
IT_陈寒2 小时前
Python中的这个可变默认参数陷阱我居然又踩了
前端·人工智能·后端
qiao若huan喜2 小时前
13、webgl基本概念 + 绘制狮子座星空
前端·javascript·信息可视化·webgl
之歆2 小时前
Day03_HTML 列表、表格、表单完整指南(上)
前端·html