Grid 布局

系统掌握 CSS Grid 二维布局模型:显式网格与隐式网格、fr 单位与 minmax() 的空间分配、auto-fill/auto-fit 的响应式模式、Subgrid 的继承机制,以及 Grid 在页面级布局中的最佳实践。

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 表示可用空间的等比份额。分配过程:

  1. 先计算固定尺寸(px、%、auto、min-content 等)
  2. 剩余空间按 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,无法共享轨道。


延展阅读