Skip to content

07|CSS Grid 布局与响应式

Flexbox 擅长一维布局(一行或一列),但面对二维布局——同时控制行和列——就显得笨拙。CSS Grid(网格布局)专门为二维布局设计:定义好行和列的轨道,再把元素放进指定的格子里。配合媒体查询,Grid 也是实现响应式页面的核心工具。

一、启用 Grid

css
.container {
  display: grid;
  grid-template-columns: 200px 200px 200px;   /* 三列,每列 200px */
  grid-template-rows: 100px 100px;             /* 两行,每行 100px */
  gap: 16px;                                   /* 行列间距 */
}

直接子元素会自动按顺序填入网格单元。上面定义了一个 3 列 × 2 行的网格,共 6 个单元格。

二、定义列和行

固定尺寸

css
.container {
  display: grid;
  grid-template-columns: 200px 200px 200px;
}

三列,每列固定 200px。如果容器宽度超过 600px,右侧会出现空白;如果小于 600px,列会溢出。

比例分配(fr)

css
.container {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;   /* 左:中:右 = 1:2:1 */
}

fr(fraction,分数)表示可用空间的比例。总空间减去固定尺寸和 gap 后,按 fr 值分配。上面中间列的宽度是左右列的两倍。

css
/* 侧边栏固定,主内容自适应 */
.container {
  display: grid;
  grid-template-columns: 240px 1fr;
}

repeat 简写

css
.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);   /* 等同于 1fr 1fr 1fr */
}

第一个参数是重复次数,第二个参数是轨道尺寸。

css
/* 自动填充:每列最小 200px,能放多少放多少 */
.container {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 16px;
}

auto-fill 会根据容器宽度自动计算能放多少列。minmax(200px, 1fr) 表示每列至少 200px,剩余空间平均分配。

三、放置元素

默认情况下,子元素按文档顺序依次填入网格。也可以显式指定位置:

css
.item-a {
  grid-column: 1 / 3;    /* 从第 1 条列线到第 3 条列线,占据两列 */
  grid-row: 1 / 2;       /* 第 1 行 */
}

.item-b {
  grid-column: 3 / 4;    /* 第 3 列 */
  grid-row: 1 / 3;       /* 从第 1 条行线到第 3 条行线,占据两行 */
}

网格线编号从 1 开始。3 列有 4 条列线,2 行有 3 条行线。

也可以用 span 表示跨多少轨道:

css
.item-wide {
  grid-column: span 2;   /* 从当前位置开始,跨两列 */
}

命名区域

对于复杂布局,可以给区域命名:

css
.container {
  display: grid;
  grid-template-columns: 200px 1fr 200px;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header header  header"
    "nav    main    aside"
    "footer footer  footer";
  gap: 16px;
}

.header { grid-area: header; }
.nav    { grid-area: nav; }
.main   { grid-area: main; }
.aside  { grid-area: aside; }
.footer { grid-area: footer; }

grid-template-areas 用字符串直观描述布局结构。同一行内用空格分隔,. 表示空单元格。

css
/* 空出右上角 */
grid-template-areas:
  "header header  ."
  "nav    main    aside"
  "footer footer  footer";

四、对齐

Grid 的对齐属性与 Flexbox 类似,但作用于网格轨道:

容器级别

css
.container {
  display: grid;
  justify-content: center;   /* 整个网格在容器中水平居中 */
  align-content: center;     /* 整个网格在容器中垂直居中 */
}

单元格级别

css
.container {
  display: grid;
  justify-items: center;     /* 所有单元格内容水平居中 */
  align-items: center;       /* 所有单元格内容垂直居中 */
}

也可以对单个项设置:

css
.item {
  justify-self: end;     /* 这个项在单元格内靠右 */
  align-self: center;    /* 这个项在单元格内垂直居中 */
}

五、响应式与媒体查询

响应式设计的核心是:同一套 HTML,在不同屏幕尺寸下呈现不同的布局和样式。媒体查询是实现这一能力的 CSS 机制。

基本语法

css
/* 默认样式(移动端优先) */
.container {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
}

/* 屏幕宽度 >= 768px 时 */
@media (min-width: 768px) {
  .container {
    grid-template-columns: repeat(2, 1fr);
    gap: 16px;
  }
}

/* 屏幕宽度 >= 1024px 时 */
@media (min-width: 1024px) {
  .container {
    grid-template-columns: repeat(3, 1fr);
    gap: 24px;
  }
}
断点宽度典型设备
< 768px手机
sm≥ 768px平板竖屏
md≥ 1024px平板横屏/小桌面
lg≥ 1280px桌面
xl≥ 1536px大桌面

上面的数值是常见约定,实际项目中可以根据内容和设计稿调整。

移动优先 vs 桌面优先

移动优先(推荐):先写移动端样式,再用 min-width 媒体查询逐步增强:

css
/* 基础:单列 */
.grid { grid-template-columns: 1fr; }

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

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

桌面优先:先写桌面样式,再用 max-width 媒体查询降级:

css
/* 基础:三列 */
.grid { grid-template-columns: repeat(3, 1fr); }

/* 小屏幕:两列 */
@media (max-width: 1023px) {
  .grid { grid-template-columns: repeat(2, 1fr); }
}

/* 手机:单列 */
@media (max-width: 767px) {
  .grid { grid-template-columns: 1fr; }
}

移动优先更符合现代开发习惯:手机用户通常网络更慢、设备性能更弱,优先加载和渲染移动端样式能提升首屏体验。此外,移动端样式通常更简单,代码量更少。

其他媒体特性

css
/*  prefers-color-scheme: 检测系统深色模式 */
@media (prefers-color-scheme: dark) {
  body {
    background-color: #1a1a1a;
    color: #e0e0e0;
  }
}

/* 打印样式 */
@media print {
  .no-print { display: none; }
  body { font-size: 12pt; }
}

六、viewport 设置

响应式页面需要在 head 中加入 viewport meta 标签:

html
<meta name="viewport" content="width=device-width, initial-scale=1.0">
作用
width=device-width视口宽度等于设备屏幕宽度
initial-scale=1.0初始缩放比例为 1
maximum-scale=1.0禁止用户缩放(不建议,影响无障碍访问)

缺少这个标签时,移动端浏览器会按桌面宽度(通常是 980px)渲染页面,导致文字过小、需要手动缩放。

七、常见场景

响应式卡片网格

css
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 16px;
}

每列至少 280px,能放多少放多少。容器变宽时自动增加列数,变窄时减少——不需要媒体查询。

经典圣杯布局

css
.layout {
  display: grid;
  grid-template-columns: 240px 1fr 240px;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header header  header"
    "nav    main    aside"
    "footer footer  footer";
  min-height: 100vh;
  gap: 16px;
}

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

桌面端三栏,平板和手机端垂直堆叠。

八、常见错误

Grid 和 Flex 混用导致困惑

css
/* 错误:在 grid 项里又用 flex 做一维对齐,其实 align-self 就够了 */
.grid-item {
  display: flex;
  align-items: center;
}

/* 正确:直接用 grid 的对齐属性 */
.container {
  display: grid;
  align-items: center;
}

忘记 viewport meta

页面在手机上显示为桌面版缩小效果,文字无法阅读。检查 head 中是否包含:

html
<meta name="viewport" content="width=device-width, initial-scale=1.0">

固定宽度破坏响应式

css
/* 错误:在大屏幕上居中,在小屏幕上溢出 */
.container {
  width: 1200px;
}

/* 正确:最大宽度限制,小屏幕自适应 */
.container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 16px;
}

媒体查询断点过于细碎

css
/* 不建议:断点太多,维护困难 */
@media (min-width: 320px) { ... }
@media (min-width: 375px) { ... }
@media (min-width: 414px) { ... }

/* 建议:按内容自然断裂,通常 3~4 个断点足够 */
@media (min-width: 768px) { ... }
@media (min-width: 1024px) { ... }