CSS Grid 网格布局:二维布局神器,轻松实现复杂网页排版

写在前面

上一篇文章我们聊了 Flex------它解决的是一维布局问题:沿着一条主轴排布元素。但真实的网页排版几乎都是二维的:既要管横排、又要管竖排;既要有固定的侧栏,又要有自适应的主区;既要有跨多列的标题,又要有多行多列的卡片墙。过去遇到这种需求,要么用 Flex 嵌套(一层套一层,DOM 变臃肿),要么退回 float 加各种 hack。

CSS Grid 是第一个真正为布局设计 的 CSS 模块:它允许你同时定义行和列,把页面划分成规则的二维网格,再让元素精确地"落"到网格的任意位置------甚至跨越多个单元格。它与 Flex 并不对立:Flex 管一维(组件内部排布),Grid 管二维(页面级骨架),二者配合才是现代 CSS 布局的完整答案。

Grid 已被所有主流浏览器完整支持多年,是"基础能力"而非"实验特性"。本文从核心概念讲到实战案例与踩坑,配合可运行代码,帮你把 Grid 一次学透。


一、Grid 的核心概念:先理解"网格"长什么样

Grid 的核心是一套几何词汇,先花两分钟搞懂它们,后面所有属性都会变得顺理成章。

1.1 六大基础术语

复制代码
┌─────────┬─────────┬─────────┐
│ 网格单元 │ 网格单元 │ 网格单元 │ ← 第1行
├─────────┼─────────┼─────────┤
│ 网格单元 │ 网格单元 │ 网格单元 │ ← 第2行
└─────────┴─────────┴─────────┘
   ↑第1列    ↑第2列    ↑第3列
术语 英文 说明
网格容器 Grid Container 设置 display: grid 的元素
网格项目 Grid Item 容器的直接子元素
网格轨道 Grid Track 一条行或一条列(用 grid-template-rows/columns 定义)
网格线 Grid Line 轨道之间的分界线,用于定位元素
网格单元 Grid Cell 行与列交叉出的最小格子
网格区域 Grid Area 一个或多个单元格组成的矩形区域
网格间隙 Grid Gap 轨道之间的间距

1.2 网格线的编号规则

网格线是从 1 开始编号的。以 3 列网格为例,垂直方向有 4 条网格线(1~4),水平方向同理。

复制代码
第1线  第2线  第3线  第4线
  │      │      │      │
  ├──────┼──────┼──────┤
  │      │      │      │

元素跨列就是"从第几条线到第几条线"。注意:网格线编号从左到右、从上到下是 1, 2, 3...;从右到左、从下到上则是 -1, -2, -3... ,负号编号在不知道网格有多少条线时非常有用(比如"贴最右边"就是 -1)。

1.3 显式网格 vs 隐式网格

  • 显式网格 :你通过 grid-template-columns/rows 明确定义的轨道。
  • 隐式网格:项目数量超出显式网格时,Grid 自动创建的那些额外轨道。

理解这两者的区别,是后面理解 grid-auto-rowsgrid-auto-flow 的前提。


二、开启 Grid:display 的两种形态

css 复制代码
/* 容器占满整行(块级) */
display: grid;

/* 容器表现为行内元素,但内部仍是网格布局 */
display: inline-grid;
html 复制代码
<div class="grid">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
  <div class="item">4</div>
</div>
css 复制代码
.grid {
  display: grid;
  grid-template-columns: 100px 100px;   /* 两列,每列 100px */
}

设置 display: grid 后,项目的默认行为:

  1. 子元素变成网格项目,默认占一个网格单元
  2. 默认只有一列,会撑满容器宽度(不写 grid-template-columns 时,项目按行堆叠,类似 block)。
  3. 项目不再受 floatvertical-align 影响。
  4. 项目默认按"先行后列"的顺序自动放置(grid-auto-flow: row)。

三、容器属性:搭建网格的"图纸"

容器属性负责"画出网格线"。这是 Grid 与 Flex 最大的不同------先定义结构,再放元素

3.1 grid-template-columns / grid-template-rows:定义轨道

这是 Grid 最核心的属性,用空格分隔的一组值来定义多条轨道。

css 复制代码
.grid {
  display: grid;
  grid-template-columns: 100px 200px 100px;  /* 3 列,宽 100/200/100 */
  grid-template-rows: 80px 80px;             /* 2 行,各 80px */
}
html 复制代码
<div class="grid">
  <div>1</div><div>2</div><div>3</div>
  <div>4</div><div>5</div><div>6</div>
</div>

3.2 fr 单位:弹性轨道(Grid 的灵魂)

fr(fraction)表示"剩余空间的份额",是 Grid 最常用的单位。它类似 Flex 里的 flex-grow,但作用于整个轨道。

css 复制代码
.grid {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;   /* 三等分宽 → 中间列是两侧的 2 倍 */
}

1fr 2fr 1fr 的含义:容器宽度分成 4 份,左右各占 1 份,中间占 2 份。fr 只在分配剩余空间,所以可以和固定值混用:

css 复制代码
.grid {
  grid-template-columns: 240px 1fr;   /* 左栏固定 240px,右栏吃满剩余 */
}

3.3 repeat() 函数:批量生成相同轨道

css 复制代码
.grid {
  grid-template-columns: repeat(3, 1fr);   /* 等价于 1fr 1fr 1fr */
  grid-template-columns: repeat(4, 100px); /* 4 列,各 100px */
}

repeat() 还支持更灵活的写法:

css 复制代码
/* 重复一个片段:repeat(2, 1fr 2fr) → 1fr 2fr 1fr 2fr */
.grid {
  grid-template-columns: repeat(2, 1fr 2fr);
}

3.4 minmax():轨道尺寸的"弹性范围"

minmax(min, max) 让轨道在最小值和最大值之间弹性伸缩,是做响应式布局的关键。

css 复制代码
.grid {
  grid-template-columns: repeat(3, minmax(150px, 1fr));
  /* 每列至少 150px,最多 1fr(平均分配) */
}

minmaxauto-fit/auto-fill 组合,能实现"列数随容器宽度自动增减"的经典效果(见实战篇)。

3.5 grid-template-areas:用命名区域"画"布局

这是 Grid 最直观的用法:给每个单元格起名字,然后用 ASCII 图一样的写法描述布局。

css 复制代码
.grid {
  display: grid;
  grid-template-columns: 200px 1fr;
  grid-template-rows: 60px 1fr 60px;
  grid-template-areas:
    "header header"
    "sidebar main"
    "footer footer";
}

.header  { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main    { grid-area: main; }
.footer  { grid-area: footer; }
html 复制代码
<div class="grid">
  <header class="header">头部</header>
  <aside class="sidebar">侧栏</aside>
  <main class="main">主内容</main>
  <footer class="footer">页脚</footer>
</div>

规则要点:

  • 同一行的名称用空格隔开,同一列的名称上下对齐
  • .(点)表示空白单元格。
  • 每个命名区域必须是一个连续的矩形(不能是 L 形或分离的)。
  • 未命名区域的元素会按自动放置规则找位置。
css 复制代码
/* 带空白的写法:中间挖空 */
grid-template-areas:
  "a . b"
  "a . b";

3.6 grid-auto-rows / grid-auto-flow:控制隐式网格

当项目超过显式网格时,Grid 自动创建隐式轨道,其尺寸由 grid-auto-rows / grid-auto-columns 控制,排列方向由 grid-auto-flow 控制。

css 复制代码
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);   /* 只定义了 3 列 */
  grid-auto-rows: 100px;                    /* 隐式行统一 100px */
  grid-auto-flow: row;                      /* 默认:先填行,再换下一行 */
}
html 复制代码
<!-- 12 个项目,显式网格只有 3 列 × 1 行,其余 9 个自动进入隐式行 -->
<div class="grid">
  <div>1</div><div>2</div>...<div>12</div>
</div>

grid-auto-flow 可选值:

说明
row(默认) 先填满一行,再往下换行
column 先填满一列,再往右换列
row dense / column dense 允许"回填"空隙,让布局更紧凑(可能会打乱顺序)

dense 适合图片画廊这类"不介意顺序、只求紧凑"的场景;要求元素按顺序排布的布局不要用。

3.7 gap:轨道间距

与 Flex 的 gap 完全一致,无需 margin hack。

css 复制代码
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;          /* 行与列统一 16px */
  /* row-gap / column-gap 可分别设置 */
}

3.8 justify-items / align-items:单元格内的对齐

与 Flex 的 align-items 类似,但作用于每个网格单元内部justify-items 管水平(行轴),align-items 管垂直(列轴)。

css 复制代码
.grid {
  justify-items: stretch;   /* 默认,项目拉伸填满单元格 */
  justify-items: center;
  justify-items: start;
  justify-items: end;

  align-items: stretch;     /* 默认 */
  align-items: center;
}
css 复制代码
/* 让所有项目在各自单元格内水平垂直居中 */
.grid {
  justify-items: center;
  align-items: center;
}

3.9 justify-content / align-content:网格整体的对齐

当网格总尺寸小于容器 时,用它们控制网格整体在容器中的位置(类似 Flex 的 justify-content)。

css 复制代码
.grid {
  grid-template-columns: 200px 200px;   /* 总宽 400px < 容器宽 */
  justify-content: center;              /* 网格整体水平居中 */
  align-content: center;                /* 网格整体垂直居中 */
}

可选值:start / center / end / space-between / space-around / space-evenly

3.10 grid-template 简写

grid-templategrid-template-rows + grid-template-columns + grid-template-areas 的合并简写:

css 复制代码
.grid {
  grid-template:
    "header header" 60px
    "sidebar main"  1fr
    "footer footer" 60px
    / 200px 1fr;
}
/* 等价于分开写 rows / columns / areas */

四、项目属性:让元素"落"到网格里

容器画好网格线之后,项目属性负责指定每个元素占据哪些单元格

4.1 grid-column / grid-row:按网格线编号定位

语法:grid-column: <起始线> / <结束线>grid-row 同理)。

css 复制代码
.item {
  grid-column: 1 / 3;   /* 从第 1 条线到第 3 条线 → 跨 2 列 */
  grid-row: 1 / 2;      /* 只占第 1 行 */
}
html 复制代码
<div class="grid">
  <div class="banner">banner 跨两列</div>
  <div>其他项目自动排</div>
  <div>其他项目自动排</div>
</div>
css 复制代码
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}
.banner { grid-column: 1 / 3; }

span 关键字表达"跨几格",语义更清晰:

css 复制代码
.banner {
  grid-column: span 2;   /* 跨 2 列(自动选择起点) */
  grid-row: span 1;      /* 占 1 行 */
}

用负编号"贴边":

css 复制代码
.right-panel {
  grid-column: -2 / -1;   /* 从右数第 2 条线到最右线 */
}

4.2 grid-area:命名区域 / 四值简写

grid-area 有两种用法:

用法一:引用命名区域 (配合容器的 grid-template-areas):

css 复制代码
.header { grid-area: header; }

用法二:四值简写 ,顺序为 row-start / column-start / row-end / column-end

css 复制代码
.item {
  grid-area: 1 / 1 / 3 / 3;   /* 从第1行第1列 到 第3行第3列 → 2×2 区域 */
}

4.3 justify-self / align-self:单个项目的单元格内对齐

覆盖容器的 justify-items / align-items,只作用于当前项目。

css 复制代码
.item {
  justify-self: center;   /* 水平居中 */
  align-self: end;        /* 贴单元格底部 */
}

4.4 order:调整项目顺序

与 Flex 的 order 语义一致,数值越小越靠前。

css 复制代码
.item {
  order: -1;   /* 排到最前 */
}

五、实战:6 个高频布局一次写清

5.1 三行代码搞定经典栅格

这是 Grid 最惊艳的地方------一个等宽栅格只需要三行:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<style>
  body { margin: 0; font-family: system-ui, sans-serif; }
  .grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 16px;
    padding: 24px;
  }
  .card { background: #f0f4ff; border: 1px solid #d6e0ff; border-radius: 8px; padding: 20px; text-align: center; }
</style>
</head>
<body>
  <div class="grid">
    <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 class="card">6</div>
  </div>
</body>
</html>

5.2 12 栅格系统(响应式框架的基础)

很多 UI 框架的栅格,本质就是 Grid 的封装:

css 复制代码
.grid-12 {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 16px;
}
.col-3  { grid-column: span 3; }   /* 占 3/12 = 1/4 宽 */
.col-4  { grid-column: span 4; }   /* 占 1/3 宽 */
.col-6  { grid-column: span 6; }   /* 占 1/2 宽 */
.col-8  { grid-column: span 8; }
.col-12 { grid-column: span 12; }
html 复制代码
<div class="grid-12">
  <div class="col-4 card">col-4</div>
  <div class="col-4 card">col-4</div>
  <div class="col-4 card">col-4</div>
  <div class="col-6 card">col-6</div>
  <div class="col-6 card">col-6</div>
</div>

5.3 圣杯布局(Grid 版)------对比上一篇文章的 Flex 版

html 复制代码
<style>
  * { box-sizing: border-box; margin: 0; padding: 0; }
  body { font-family: system-ui, sans-serif; }
  .layout {
    display: grid;
    grid-template-columns: 200px 1fr 200px;
    grid-template-rows: 60px 1fr 60px;
    grid-template-areas:
      "header header header"
      "left   main   right"
      "footer footer footer";
    min-height: 100vh;
  }
  .header { grid-area: header; background: #333; color: #fff; padding: 16px; }
  .left   { grid-area: left;   background: #f5f5f5; padding: 16px; }
  .main   { grid-area: main;   background: #fff; padding: 16px; }
  .right  { grid-area: right;  background: #f5f5f5; padding: 16px; }
  .footer { grid-area: footer; background: #333; color: #fff; padding: 16px; text-align: center; }
</style>

<div class="layout">
  <header class="header">头部</header>
  <aside class="left">左栏 200px</aside>
  <main class="main">中间自适应</main>
  <aside class="right">右栏 200px</aside>
  <footer class="footer">页脚</footer>
</div>

对比 Flex 版:Flex 需要 flex: 1 + 固定宽度 + 换行处理;Grid 用 grid-template-areas 一眼就能看出整体结构,且DOM 顺序可以完全独立于视觉位置 (比如把 main 放在 HTML 最前面也照样渲染到中间)。

5.4 Dashboard 面板布局(跨行跨列)

Grid 处理"区域大小不一"的面板布局最拿手:

html 复制代码
<style>
  * { box-sizing: border-box; margin: 0; padding: 0; }
  body { font-family: system-ui, sans-serif; padding: 24px; background: #f7f8fa; }
  .dashboard {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-template-rows: 120px 120px;
    grid-auto-rows: 120px;     /* 超出部分也统一 120px */
    gap: 16px;
  }
  .panel { background: #fff; border: 1px solid #eee; border-radius: 8px; padding: 16px; display: flex; align-items: center; justify-content: center; color: #555; }
  .wide   { grid-column: span 2; }
  .tall   { grid-row: span 2; }
  .hero   { grid-column: span 2; grid-row: span 2; background: #4f8cff; color: #fff; }
</style>

<div class="dashboard">
  <div class="panel hero">主视觉(2×2)</div>
  <div class="panel">KPI 1</div>
  <div class="panel">KPI 2</div>
  <div class="panel wide">图表(跨 2 列)</div>
  <div class="panel tall">侧栏(跨 2 行)</div>
  <div class="panel">列表</div>
</div>

5.5 响应式画廊:auto-fit + minmax 自动换列

这是 Grid 最惊艳的能力之一------不写媒体查询,列数自动随容器宽度变化

html 复制代码
<style>
  * { box-sizing: border-box; margin: 0; padding: 0; }
  body { font-family: system-ui, sans-serif; padding: 24px; background: #f7f8fa; }
  .gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 12px;
  }
  .photo { background: #4f8cff; color: #fff; border-radius: 8px; aspect-ratio: 1; display: flex; align-items: center; justify-content: center; }
</style>

<div class="gallery">
  <div class="photo">1</div><div class="photo">2</div><div class="photo">3</div>
  <div class="photo">4</div><div class="photo">5</div><div class="photo">6</div>
</div>

试试缩放窗口:容器宽时一排放 56 张,窄时自动收缩到 2 3 张,无需任何媒体查询

auto-fill vs auto-fit 的区别(重要):

  • auto-fill:尽可能多地创建轨道,即使空着也算数
  • auto-fit:创建轨道后,空轨道会被折叠,让内容轨道拉伸铺满。
css 复制代码
/* auto-fill:容器很宽时右侧会有空白列(轨道空着) */
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));

/* auto-fit:空轨道被折叠,项目铺满整行 */
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));

当项目数量少、希望铺满整行时用 auto-fit;希望保持固定轨道数量时用 auto-fill

5.6 网格居中

Grid 也能做水平垂直居中(和 Flex 一样简单):

css 复制代码
.center {
  display: grid;
  place-items: center;   /* justify-items + align-items 的简写 */
}
html 复制代码
<div class="center" style="height: 100vh;">
  <div>居中的内容</div>
</div>

place-itemsjustify-itemsalign-items 的合并简写,一行代码搞定双轴居中。


六、Grid vs Flex:什么时候用哪个?

承接上一篇文章的结论:Flex 是一维,Grid 是二维。更准确的表述是------

维度 Flexbox CSS Grid
布局模型 一维(沿主轴) 二维(行 + 列同时)
设计思路 内容驱动:项目沿主轴流动、自动伸缩 结构驱动:先定义网格线,再放置元素
元素定位 靠顺序 + 对齐,难精确控制位置 可精确指定行列范围、跨行跨列
典型场景 导航、居中、列表、组件内部排布 页面骨架、栅格系统、Dashboard、画廊
重叠元素 不擅长 grid-area 让元素占同一区域可叠加
自动换行 flex-wrap + 宽度计算 repeat(auto-fit, minmax(...)) 一行搞定

选择建议

  • 只是横向或纵向的排布(导航、按钮组、列表行、垂直居中)→ Flex
  • 需要同时管行列、需要元素跨行跨列、需要"画"出整个页面结构 → Grid
  • 组件内部用 Flex 排细节,页面级用 Grid 定骨架 → 两者结合,这是现代 CSS 布局的标准姿势。

七、高频踩坑与最佳实践

7.1 坑 1:隐式网格尺寸"失控"

只定义了 grid-template-columns,没定义行高,超出部分的行默认是 auto(由内容撑开),看起来会参差不齐。

css 复制代码
/* 只写了列,行是隐式的 */
.grid { grid-template-columns: repeat(3, 1fr); }

/* 修复:给隐式行统一尺寸 */
.grid { grid-template-columns: repeat(3, 1fr); grid-auto-rows: 100px; }

7.2 坑 2:auto-fill 与 auto-fit 用错

项目少、希望铺满时用了 auto-fill,会出现右侧空白列;项目多、希望换行时两者差异不大。不确定时优先 auto-fit(更符合"铺满"直觉)。

7.3 坑 3:fr 与 % / px 混用的陷阱

fr 分配的是剩余空间100px 1fr 是"100px 固定,剩余给 1fr";但 50% 1fr 则是"50% 固定,1fr 拿剩下的 50%"------两列永远不满容器(除非 1fr 恰好等于 50%)。需要等分时直接用 repeat(2, 1fr)repeat(2, minmax(0, 1fr))

7.4 坑 4:minmax 最小值和内容溢出

minmax(150px, 1fr) 中的最小值是硬下限,如果容器比 150px × 列数 还窄,网格会溢出。小屏防护:

css 复制代码
/* 用 minmax(0, 1fr) 允许轨道缩到 0 */
.grid { grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); }

7.5 坑 5:grid-template-areas 的矩形限制

命名区域必须连续、必须矩形,不能出现 L 形或断开的同名区域。需要复杂形状时改用 grid-column / grid-row 的编号定位。

7.6 坑 6:网格线编号方向

网格线从 1 开始,负号从终点往回数。容易记混的是 grid-column: 1 / 3 是"跨 2 列"(3-1=2),而不是占 3 列。span 表达跨格数更不容易出错

7.7 最佳实践小结

  1. 页面骨架用 Grid,组件内部用 Flex,各司其职。
  2. 轨道尺寸优先用 frminmax(),少用百分比。
  3. 能用 grid-template-areas 就不手写编号,可读性最高。
  4. 响应式优先 auto-fit + minmax(0, 1fr),大部分情况能免掉媒体查询。
  5. 给隐式网格统一 grid-auto-rows,避免尺寸失控。
  6. 间距一律 gap,别再用 margin hack。

八、总结

Grid 的学习路径比 Flex 多一个层次:Flex 只需要"容器管排布、项目管伸缩",Grid 还需要"先画网格线、再定位元素"。但也正因为"结构先行",Grid 表达复杂布局的能力远超 Flex。

把整篇文章收敛成几条主线:

  1. 开网格display: grid
  2. 画轨道grid-template-columns/rows,配合 repeat()frminmax()
  3. 命名区域grid-template-areas + grid-area,可读性之王。
  4. 隐式网格 :超出部分用 grid-auto-rows / grid-auto-flow 控制。
  5. 放元素grid-column / grid-row / grid-area,用 span 表达跨格。
  6. 对齐 :单元格内 justify/align-items(简写 place-items),整体 justify/align-content
  7. 间距 :一律 gap
  8. 响应式repeat(auto-fit, minmax(0, 1fr)) 一行搞定。

Flex 解决一维的"排",Grid 解决二维的"布"------把这两件工具用熟,再加上几行媒体查询收尾,绝大多数网页排版都能轻松拿下。


相关推荐
lhldsg18 分钟前
酒吧点餐小程序开发:从需求分析到技术落地实战
java·前端·小程序·架构·交友
Csvn18 分钟前
微前端样式隔离:为什么你的 CSS 串到别人家了?Shadow DOM、CSS Variables 与 @layer 实战
前端
金花顺21 分钟前
android_media_AudioTrack
前端·面试
木公子21 分钟前
Vue3源码精读04:Scheduler 调度器深度解析|异步批量更新与任务队列源码全解
前端·vue.js
YHL22 分钟前
🧠 React useContext 深度解析:告别 Props 层层传递的噩梦
前端·react.js
萧行之32 分钟前
Observalble Plot和图形语法理论
前端·数据可视化
开开心心就好34 分钟前
手机悬屏翻译工具外语游戏漫画APP全覆盖
android·前端·javascript·python·游戏·pdf·html
支支დ41 分钟前
Phind 前端特定优势:AI 编程助手的体验革新
前端
DDXYcoder1 小时前
万字解析动态内存从编译到运行
java·前端·算法