网格布局

1 阅读8分钟

CSS Grid 布局完全指南

一、什么是 Grid

Grid(网格布局)是 CSS 的二维布局系统,可以同时控制行和列。与 Flexbox(一维,只控制行或列中的一个方向)不同,Grid 让你在预定义的网格上精确放置元素。

一句话理解:把容器想象成一张表格,你可以随意定义行、列的数量和大小,然后让子元素占据任意单元格。


二、核心概念

网格线(Grid Line)

定义网格的分界线,从 1 开始编号。

    1      2      3      4
    | col1 | col2 | col3 |
 1  +------+------+------+
    |      |      |      |
    | row1 |      |      |
 2  +------+------+------+
    |      |      |      |
    | row2 |      |      |
 3  +------+------+------+
  • 竖线是列线(column line),共 4 条
  • 横线是行线(row line),共 3 条
  • 可以用负数从末尾反向编号:-1 表示最后一条线

网格轨道(Grid Track)

两条相邻网格线之间的空间,即一行或一列。

网格单元格(Grid Cell)

四条网格线围成的最小单位。

网格区域(Grid Area)

一个或多个单元格组成的矩形区域。


三、激活 Grid

.container {
  display: grid;    /* 块级网格 */
  /* 或 */
  display: inline-grid; /* 行内网格 */
}

激活后,直接子元素自动成为网格项目(grid item)。


四、容器属性

以下所有属性都设置在父容器(.container)上。

4.1 定义行和列

grid-template-columns

定义列的宽度和数量。

.container {
  /* 固定宽度:3 列 */
  grid-template-columns: 200px 1fr 200px;

  /* 等宽 3 列 */
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-columns: repeat(3, 1fr);  /* 等价写法 */

  /* 不等宽 */
  grid-template-columns: repeat(3, 1fr) repeat(2, 2fr);
  /* 结果: 1fr 1fr 1fr 2fr 2fr */

  /* 最小-最大范围 */
  grid-template-columns: repeat(3, minmax(100px, 1fr));

  /* 混合单位 */
  grid-template-columns: 200px 1fr auto;
}
grid-template-rows

定义行的高度和数量。

.container {
  grid-template-rows: 60px 1fr 40px;     /* 顶栏 60px,中间自适应,底栏 40px */
  grid-template-rows: repeat(4, 100px);  /* 4 行,每行 100px */
  grid-template-rows: auto 1fr auto;     /* 内容撑开 */
}

4.2 fr 单位

fr(fraction)代表剩余自由空间的份额

grid-template-columns: 1fr 2fr 1fr;
/* 总空间分成 1+2+1=4 份,第二列占 2/4 */

注意fr 分配的是剩余空间,如果有固定宽度的列,会先扣除。

grid-template-columns: 200px 1fr 1fr;
/* 先扣除 200px,剩余空间平分给两个 1fr */

4.3 常用函数

函数说明示例
repeat(n, value)重复模式repeat(3, 1fr)
minmax(min, max)最小-最大值范围minmax(100px, 1fr)
min(a, b)取两者较小值min(50%, 300px)
max(a, b)取两者较大值max(200px, 20vw)
fit-content(limit)内容自适应,不超过 limitfit-content(300px)
clamp(min, ideal, max)理想值在范围内自适应clamp(200px, 50%, 600px)

4.4 自动填充 — 响应式利器

.container {
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}

原理:每列至少 200px,在容器宽度内尽可能多地创建列,剩余空间平均分配(1fr)。

auto-fill vs auto-fit
auto-fill(容器 900px,每列 min 200px):
┌──────┬──────┬──────┬──────┐ ← 创建 4 列,第 4 列是空的但保留空间
│  123   │      │
└──────┴──────┴──────┴──────┘

auto-fit:
┌──────┬──────┬──────┐ ← 只创建 3 列(因为只需要 3 列装内容)
│  123   │ ← 空列折叠,现有列扩展填满
└──────┴──────┴──────┘

经验法则:大多数情况下用 auto-fit,效果更符合直觉。


4.5 命名区域 — grid-template-areas

用可视化的方式定义布局。

.container {
  grid-template-areas:
    "header  header  header"
    "sidebar main    main"
    "footer  footer  footer";
  grid-template-columns: 200px 1fr 1fr;
  grid-template-rows: 60px 1fr 40px;
}

.header  { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main    { grid-area: main; }
.footer  { grid-area: footer; }

规则

  • 每行用一对引号包裹,列之间用空格分隔
  • 每行的列数必须相同
  • . 表示空白区域
grid-template-areas:
  "header header"
  ".      main"   /* 左侧留空 */
  "footer footer";

4.6 间距 — gap

.container {
  gap: 20px;           /* 行间距和列间距都是 20px */
  gap: 20px 10px;      /* 行间距 20px,列间距 10px */
  row-gap: 20px;       /* 仅行间距 */
  column-gap: 10px;    /* 仅列间距 */
}

gap 只作用于网格内部,不影响容器边缘。


4.7 自动放置 — grid-auto-flow

当项目没有明确指定位置时,如何自动排列。

.container {
  grid-auto-flow: row;    /* 默认,从左到右逐行填充 */
  grid-auto-flow: column; /* 从上到下逐列填充 */
  grid-auto-flow: dense;  /* 紧密填充,填补前面的空隙 */
}

4.8 自动轨道大小

当项目超出定义的网格时,自动创建的轨道大小。

.container {
  grid-template-columns: 100px 100px;  /* 只定义了 2 列 */
  grid-auto-columns: 1fr;              /* 第 3 列及以后宽度为 1fr */
  grid-auto-rows: 80px;                /* 新增行高 80px */
}

4.9 单元格内对齐(影响所有项目)

justify-items — 水平对齐
.container {
  justify-items: start;   /* 靠左 */
  justify-items: end;     /* 靠右 */
  justify-items: center;  /* 居中 */
  justify-items: stretch; /* 拉伸填满(默认) */
}
align-items — 垂直对齐
.container {
  align-items: start;   /* 靠上 */
  align-items: end;     /* 靠下 */
  align-items: center;  /* 居中 */
  align-items: stretch; /* 拉伸填满(默认) */
}
place-items — 简写
.container {
  place-items: center;         /* 水平垂直都居中 */
  place-items: center stretch; /* 垂直居中,水平拉伸 */
}

4.10 整个网格在容器中的对齐

当网格总大小小于容器大小时生效。

.container {
  justify-content: center;        /* 水平居中 */
  justify-content: space-between; /* 列之间均匀分布 */
  align-content: center;          /* 垂直居中 */
  align-content: space-around;    /* 行之间均匀分布 */
}

五、项目属性

以下属性设置在网格的直接子元素(item)上。

5.1 指定位置 — grid-column / grid-row

.item {
  /* 从第 1 条列线到第 3 条列线 */
  grid-column-start: 1;
  grid-column-end: 3;
  /* 简写 */
  grid-column: 1 / 3;

  /* 跨越 2 行 */
  grid-row: 1 / span 2;

  /* 从结尾倒数第 1 条线到倒数第 3 条线 */
  grid-column: -1 / -3;

  /* 横跨所有列 */
  grid-column: 1 / -1;
}
grid-column: 2 / 4  的效果:

    1      2      3      4
    |      |██████|██████|      ← 从线 2 到线 4
    |      |██████|██████|

5.2 命名区域 — grid-area

.item {
  grid-area: header;          /* 引用 grid-template-areas 中的名称 */
  grid-area: 1 / 1 / 3 / 3;   /* row-start / col-start / row-end / col-end */
}

5.3 单个项目对齐

覆盖容器的 justify-items / align-items 设置。

.item {
  justify-self: center;  /* 水平居中 */
  align-self: end;       /* 垂直靠下 */
  /* 简写 */
  place-self: center end; /* 垂直居中,水平靠右 */
}

六、网格线编号规则

    1        2        3        4        ← 正数
    |  col1  |  col2  |  col3  |
   -4       -3       -2       -1        ← 负数
  • 编号从 1 开始,不是从 0
  • 负数从末尾倒数:-1 是最后一条线
  • 可以给网格线命名
grid-template-columns: [sidebar-start] 200px [sidebar-end main-start] 1fr [main-end];

七、min-content / max-content

grid-template-columns: min-content 1fr max-content;
含义
min-content内容所需最小空间(如一个单词的宽度)
max-content内容所需最大空间(如不换行的完整文本宽度)
auto默认,根据内容自动计算

八、常见布局模式

8.1 响应式卡片网格

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 20px;
}

一行 CSS 实现响应式卡片,无需任何 @media 查询。

8.2 经典页面布局

.page {
  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;
}

8.3 不规则画廊

.gallery {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 100px;
  gap: 10px;
}

.wide  { grid-column: span 2; }           /* 宽 2 格 */
.tall  { grid-row: span 2; }              /* 高 2 格 */
.featured { grid-column: span 2; grid-row: span 2; }  /* 2×2 大方块 */

8.4 完美居中

.center {
  display: grid;
  place-items: center;
  height: 100vh;
}

8.5 固定侧边栏 + 自适应主内容

.app {
  display: grid;
  grid-template-columns: 250px 1fr;
  grid-template-rows: auto 1fr;
  height: 100vh;
}

.app > header { grid-column: 1 / -1; }   /* header 横跨所有列 */

8.6 Holy Grail Layout(圣杯布局)

.holy-grail {
  display: grid;
  grid-template-columns: 150px 1fr 150px;
  grid-template-rows: auto 1fr auto;
  min-height: 100vh;
  gap: 10px;
}

.holy-grail > header { grid-column: 1 / -1; grid-row: 1; }
.holy-grail > nav    { grid-column: 1;    grid-row: 2; }
.holy-grail > main   { grid-column: 2;    grid-row: 2; }
.holy-grail > aside  { grid-column: 3;    grid-row: 2; }
.holy-grail > footer { grid-column: 1 / -1; grid-row: 3; }

九、Grid vs Flexbox

GridFlexbox
维度二维(行 + 列)一维(行 或 列)
先定义布局还是内容先定义网格(容器驱动)内容决定大小(内容驱动)
适用场景页面框架、卡片网格、复杂布局导航栏、按钮组、列表、单行/列
重叠元素支持(通过相同区域)不支持
学习曲线较陡较平缓

最佳实践:外层用 Grid 做整体框架,内部组件用 Flexbox 做细节布局。两者配合使用。


十、实用技巧

隐式网格线

即使没有显式定义第 5 条线,你也可以使用它:

grid-template-columns: 1fr 1fr 1fr;  /* 定义了 4 条线 */
.item { grid-column: 1 / 5; }        /* 可以使用 5,会自动扩展 */

span 关键字

grid-column: span 2;      /* 从当前位置向右跨 2 列 */
grid-column: span 2 / 4;  /* 到第 4 条线结束,向左跨 2 列 */

dense 填充算法

grid-auto-flow: row dense;

让后面的小项目填补前面留下的空隙(可能造成视觉顺序混乱,适合数据看板)。

层叠(Overlap)

多个项目可以占据相同的网格区域,实现层叠效果:

.image  { grid-area: 1 / 1 / 3 / 3; }
.caption { grid-area: 2 / 2 / 3 / 3; z-index: 1; }