CSS 布局深度:Flexbox 与 Grid 完全指南

2024-08-27 前端

前言

CSS 布局经历了从表格布局(<table>)到浮动布局(float),再到定位布局(position)的演进。每种方法都有各自的局限。直到 Flexbox(弹性盒子)和 Grid(网格布局)的出现,CSS 才真正拥有了现代、强大且直观的布局系统。

本文将用 8000+ 字的篇幅,从原理到实战,深入剖析 Flexbox 和 Grid 的每一项核心属性、计算规则以及经典的布局方案。每个知识点都配有完整可运行的 HTML/CSS 代码,你可以直接复制到浏览器中验证。


第一章:Flexbox 详解

Flexbox(Flexible Box Layout Module)是 CSS3 引入的一维布局模型。所谓"一维",是指它一次只处理一个方向的排列——要么是水平方向的行(row),要么是垂直方向的列(column)。

1.1 基础概念

Flexbox 由两个核心要素构成:

  • Flex 容器(flex container):设置了 display: flex 或 display: inline-flex 的父元素。
  • Flex 项目(flex item):容器内的直接子元素。

有两个轴贯穿始终:

  • 主轴(main axis):由 flex-direction 决定的方向。默认为 row,主轴为水平方向,从左到右。
  • 交叉轴(cross axis):与主轴垂直的方向。
         主轴 (main axis) ──────────────────────>
   ┌──────┬──────┬──────┬──────┬──────┬──────┐
   │ item │ item │ item │ item │ item │ item │  交叉轴
   ├──────┼──────┼──────┼──────┼──────┼──────┤  (cross
   │ item │ item │ item │ item │ item │ item │   axis)
   └──────┴──────┴──────┴──────┴──────┴──────┘     │
                                                     ▼

1.2 容器属性

1.2.1 flex-direction

控制主轴方向,即项目的排列方向。

值 方向 起点
row(默认) 水平,从左到右 左边缘
row-reverse 水平,从右到左 右边缘
column 垂直,从上到下 上边缘
column-reverse 垂直,从下到上 下边缘
<div class="demo-box">
  <style>
    .flex-container {
      display: flex;
      flex-direction: row; /* row | row-reverse | column | column-reverse */
      padding: 12px;
      background: #f0f4f8;
      border-radius: 8px;
    }
    .flex-container .item {
      background: #3b82f6;
      color: white;
      padding: 16px 24px;
      margin: 4px;
      border-radius: 4px;
      font-size: 14px;
    }
  </style>
  <div class="flex-container" style="flex-direction: row;">
    <div class="item">1</div>
    <div class="item">2</div>
    <div class="item">3</div>
    <div class="item">4</div>
  </div>
  <p style="margin-top:8px;color:#666;font-size:13px;">↑ flex-direction: row(默认水平排列)</p>
</div>

1.2.2 flex-wrap

控制项目是否换行。默认情况下,所有项目排在一行。

值 行为
nowrap(默认) 不换行,项目可能被压缩
wrap 换行,从交叉轴起点开始
wrap-reverse 换行,从交叉轴终点开始

1.2.3 justify-content

定义项目在主轴上的对齐方式。这是最常用的 Flexbox 属性之一。

值 效果
flex-start(默认) 从主轴的起始端排列
flex-end 从主轴的末端排列
center 居中排列
space-between 两端对齐,项目之间的间隔相等
space-around 每个项目两侧的间隔相等
space-evenly 项目之间的间隔和项目与容器边缘的间隔都相等

1.2.4 align-items

定义项目在交叉轴上的对齐方式。

值 效果
stretch(默认) 如果项目未设置高度或宽度,拉伸占满整个容器
flex-start 从交叉轴起始端对齐
flex-end 从交叉轴末端对齐
center 交叉轴居中
baseline 项目的第一行文字基线对齐
<div class="demo-box">
  <style>
    .align-items-demo {
      display: flex;
      align-items: center;
      height: 120px;
      padding: 12px;
      background: #f0f4f8;
      border-radius: 8px;
      border: 1px dashed #ccc;
    }
    .align-items-demo .item {
      background: #f59e0b;
      color: white;
      padding: 8px 20px;
      border-radius: 4px;
      font-size: 13px;
    }
    .align-items-demo .item.tall { padding: 24px 20px; }
    .align-items-demo .item.short { padding: 4px 20px; }
  </style>
  <div class="align-items-demo" style="align-items: center;">
    <div class="item tall">高</div>
    <div class="item">正常</div>
    <div class="item short">矮</div>
    <div class="item tall">高</div>
  </div>
  <p style="margin-top:8px;color:#666;font-size:13px;">↑ align-items: center — 所有项目在交叉轴(垂直方向)居中</p>
</div>

1.2.5 align-content

当 flex-wrap 产生多行时,align-content 控制多行在交叉轴上的对齐方式。单行时该属性无效。

值 效果
stretch(默认) 行占满整个交叉轴
flex-start 从交叉轴起始端堆叠
flex-end 从交叉轴末端堆叠
center 交叉轴居中
space-between 行之间等间距
space-around 每行两侧间距相等

1.2.6 gap

gap 属性定义 flex 项目之间的间距,替代了早期的 margin hack。

.container {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;         /* 行和列间距都是 16px */
  /* gap: 16px 24px; */  /* 行间距 16px,列间距 24px */
}

注意:gap 在 Flexbox 中的浏览器支持已经很完善(Chrome 84+、Firefox 63+、Safari 14.1+),可以放心使用。

1.3 项目属性

1.3.1 flex-grow

定义项目的放大比例。默认值为 0,即即使有剩余空间也不放大。

计算规则:

  1. 计算所有项目的 flex-grow 之和
  2. 剩余空间 × (项目 flex-grow / flex-grow 总和)= 该项目的额外宽度
<div class="demo-box">
  <style>
    .grow-demo {
      display: flex;
      padding: 12px;
      background: #f0f4f8;
      border-radius: 8px;
    }
    .grow-demo .item {
      color: white;
      padding: 16px 24px;
      border-radius: 4px;
      font-size: 14px;
      text-align: center;
    }
  </style>
  <div class="grow-demo">
    <div class="item" style="background:#3b82f6; flex-grow: 1;">flex-grow: 1</div>
    <div class="item" style="background:#10b981; flex-grow: 2;">flex-grow: 2</div>
    <div class="item" style="background:#f59e0b; flex-grow: 1;">flex-grow: 1</div>
  </div>
  <p style="margin-top:8px;color:#666;font-size:13px;">
    ↑ 剩余空间按 1:2:1 分配。flex-grow:2 的绿色项目获得的空间是其他项目的两倍。
  </p>
</div>

1.3.2 flex-shrink

定义项目的缩小比例。默认值为 1,即空间不足时项目按比例缩小。

收缩规则(理解这一点至关重要):
当所有项目的总宽度超过容器宽度时,每个项目缩小的量 = 超出宽度 × (项目宽度 × flex-shrink) / (所有项目宽度×flex-shrink 之和)。

<div class="demo-box">
  <style>
    .shrink-demo {
      display: flex;
      width: 400px;
      padding: 12px;
      background: #f0f4f8;
      border-radius: 8px;
    }
    .shrink-demo .item {
      width: 200px;  /* 三项共 600px,容器 400px,超出 200px */
      color: white;
      padding: 16px;
      border-radius: 4px;
      font-size: 14px;
      text-align: center;
      box-sizing: border-box;
    }
  </style>
  <div class="shrink-demo">
    <div class="item" style="background:#3b82f6; flex-shrink: 1;">shrink:1</div>
    <div class="item" style="background:#10b981; flex-shrink: 2;">shrink:2<br>缩小更多</div>
    <div class="item" style="background:#f59e0b; flex-shrink: 0;">shrink:0<br>不缩小</div>
  </div>
  <p style="margin-top:8px;color:#666;font-size:13px;">
    ↑ 第三个项目的 flex-shrink:0,永远不会被压缩。超出的宽度由前两项按 1:2 比例分担。
  </p>
</div>

1.3.3 flex-basis

定义项目在分配剩余空间之前的初始大小。可以设为具体的长度值(px、em、%)、auto(默认,等同于项目的 width/height)或 content。

.item {
  flex-basis: 200px;  /* 初始宽度为 200px */
  /* flex-basis: 30%; */ /* 初始宽度为容器宽度的 30% */
}

当 flex-direction: column 时,flex-basis 控制的是高度。

1.3.4 flex(简写属性)

强烈推荐使用 flex 简写,而不是单独设置三个属性。

/* 语法:flex: flex-grow flex-shrink flex-basis */

.item {
  flex: 1;              /* 等价于 flex: 1 1 0% — 均匀分配空间 */
  flex: auto;           /* 等价于 flex: 1 1 auto — 按内容大小分配 */
  flex: none;           /* 等价于 flex: 0 0 auto — 固定大小,不伸缩 */
  flex: 0 0 200px;      /* 固定 200px,不伸缩 */
  flex: 1 1 300px;      /* 最小 300px,可伸缩 */
}

常见简写模式:

/* ——— 等分布局(所有项目宽度相等) ——— */
.item { flex: 1; }                     /* 所有项目 flex-grow 都为 1,等分容器宽度 */

/* ——— 固定 + 弹性组合 ——— */
.sidebar { flex: 0 0 250px; }          /* 固定 250px,不伸缩 */
.main    { flex: 1 1 0%; }            /* 占据剩余全部空间 */

/* ——— 自适应内容 ——— */
.item { flex: 0 1 auto; }             /* 根据内容确定大小,必要时收缩 */

1.3.5 align-self

允许单个项目覆盖容器的 align-items 设置。

.item:first-child {
  align-self: flex-start;  /* 只有这个项目在交叉轴顶端对齐 */
}
.item:last-child {
  align-self: flex-end;    /* 只有这个项目在交叉轴底端对齐 */
}

1.3.6 order

定义项目的排列顺序。数值越小排列越靠前,默认值为 0。

.item:nth-child(1) { order: 3; }
.item:nth-child(2) { order: 1; }  /* 最先显示 */
.item:nth-child(3) { order: 2; }

注意:order 只改变视觉顺序,不影响 DOM 顺序和键盘导航顺序。对可访问性有影响,谨慎使用。

1.4 Flex 计算规则详解

Flexbox 的计算分三步:

第一步:确定初始大小(flex-basis)

  • 如果 flex-basis 为具体值,取该值。
  • 如果 flex-basis: auto,取项目的 width/height 或内容大小。

第二步:计算剩余空间

剩余空间 = 容器大小 - 所有项目的 flex-basis 之和 - gap 总和

第三步:分配或收缩

  • 如果剩余空间 > 0:按 flex-grow 比例分配。
  • 如果剩余空间 < 0:按 flex-shrink 比例收缩。

一个完整的计算示例:

容器宽度:1000px
项目 A: flex: 1 1 200px
项目 B: flex: 2 1 100px
项目 C: flex: 1 2 300px

第一步:初始大小 = 200 + 100 + 300 = 600px
第二步:剩余空间 = 1000 - 600 = 400px
第三步:flex-grow 总和 = 1 + 2 + 1 = 4

A 最终宽度 = 200 + 400 × (1/4) = 300px
B 最终宽度 = 100 + 400 × (2/4) = 300px
C 最终宽度 = 300 + 400 × (1/4) = 400px

1.5 常见布局模式

1.5.1 水平居中

<div class="demo-box">
  <style>
    .center-h {
      display: flex;
      justify-content: center;
      padding: 16px;
      background: #f0f4f8;
      border-radius: 8px;
    }
    .center-h .box {
      background: #3b82f6;
      color: white;
      padding: 16px 32px;
      border-radius: 4px;
    }
  </style>
  <div class="center-h">
    <div class="box">水平居中</div>
  </div>
</div>

1.5.2 垂直居中(经典 flex 居中方案)

<div class="demo-box">
  <style>
    .center-v {
      display: flex;
      align-items: center;
      justify-content: center;
      height: 200px;
      background: #f0f4f8;
      border-radius: 8px;
    }
    .center-v .box {
      background: #8b5cf6;
      color: white;
      padding: 24px 40px;
      border-radius: 8px;
      text-align: center;
    }
  </style>
  <div class="center-v">
    <div class="box">完美居中<br>水平 + 垂直</div>
  </div>
</div>

1.5.3 圣杯布局(Flexbox 版)

圣杯布局指:页头(header)、页脚(footer)固定高度,中间是 左侧边栏 + 主体 + 右侧边栏 的三栏结构。

1.5.4 等分布局

<div class="demo-box">
  <style>
    .equal-grid {
      display: flex;
      gap: 12px;
      padding: 12px;
      background: #f0f4f8;
      border-radius: 8px;
    }
    .equal-grid .item {
      flex: 1;
      background: linear-gradient(135deg, #667eea, #764ba2);
      color: white;
      padding: 24px 16px;
      border-radius: 6px;
      text-align: center;
      font-size: 14px;
    }
  </style>
  <div class="equal-grid">
    <div class="item">1/4</div>
    <div class="item">1/4</div>
    <div class="item">1/4</div>
    <div class="item">1/4</div>
  </div>
  <p style="margin-top:8px;color:#666;font-size:13px;">↑ 四个项目各占 1/4,使用 flex:1 等分容器</p>
</div>

1.5.5 固定 + 弹性组合

1.6 响应式 Flexbox:flex-wrap + flex-basis

使用 flex-wrap 配合 flex-basis 可以轻松实现自适应网格。

原理:当容器宽度足够容纳 N 个 200px 的项目时,它们在一行中均匀分布(flex-grow)。当容器变窄,每行放不下时,最后一个项目换到下一行。


第二章:Grid 详解

CSS Grid Layout 是 CSS 中第一个真正的二维布局系统。它可以同时处理行和列,在宏观的页面级布局中表现出色。

2.1 基础概念

         列1        列2        列3
    ┌──────────┬──────────┬──────────┐
行1 │ grid-    │ grid-    │ grid-    │
    │ cell     │ cell     │ cell     │
    ├──────────┼──────────┼──────────┤
行2 │ grid-    │ grid-    │ grid-    │
    │ cell     │ cell     │ cell     │
    ├──────────┼──────────┼──────────┤
行3 │ grid-    │ grid-    │ grid-    │
    │ cell     │ cell     │ cell     │
    └──────────┴──────────┴──────────┘

核心术语:

  • 网格容器(grid container):display: grid
  • 网格项目(grid item):容器的直接子元素
  • 网格线(grid line):水平和垂直的分割线,用于定位
  • 网格单元格(grid cell):最小的网格单位
  • 网格区域(grid area):由任意数量的相邻单元格组成
  • 网格轨道(grid track):一行或一列

2.2 容器属性

2.2.1 grid-template-columns / grid-template-rows

定义网格的列轨道和行轨道的大小和数量。

.container {
  display: grid;
  grid-template-columns: 200px 1fr 1fr;  /* 3 列:固定 200px + 两个弹性列 */
  grid-template-rows: 100px auto 100px;   /* 3 行 */
}

2.2.2 grid-template-areas

这是 Grid 最强大的特性之一,让你可以用命名区域的方式定义布局。

.container {
  display: grid;
  grid-template-columns: 200px 1fr 200px;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header  header  header"
    "sidebar main    aside"
    "footer  footer  footer";
}
.header  { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main    { grid-area: main; }
.aside   { grid-area: aside; }
.footer  { grid-area: footer; }

规则:

  • 每一行字符串代表网格的一行
  • 每个字符串内的名称对应列
  • 用英文句号 . 表示空单元格
  • 同一区域的名称必须形成矩形,不能是 L 形
<div class="demo-box">
  <style>
    .grid-areas {
      display: grid;
      grid-template-columns: 160px 1fr 160px;
      grid-template-rows: auto 1fr auto;
      grid-template-areas:
        "header  header  header"
        "sidebar main    aside"
        "footer  footer  footer";
      gap: 8px;
      min-height: 350px;
      padding: 12px;
      background: #f0f4f8;
      border-radius: 8px;
    }
    .grid-areas .header  { grid-area: header;  background: #1e293b; color: white; padding: 16px; text-align: center; border-radius: 4px; }
    .grid-areas .sidebar { grid-area: sidebar; background: #f59e0b; color: white; padding: 16px; border-radius: 4px; }
    .grid-areas .main    { grid-area: main;    background: #f8fafc; padding: 16px; border-radius: 4px; }
    .grid-areas .aside   { grid-area: aside;   background: #10b981; color: white; padding: 16px; border-radius: 4px; }
    .grid-areas .footer  { grid-area: footer;  background: #1e293b; color: white; padding: 16px; text-align: center; border-radius: 4px; }
  </style>
  <div class="grid-areas">
    <div class="header">📰 Header(跨越三列)</div>
    <div class="sidebar">Sidebar 160px</div>
    <div class="main"><strong>Main 弹性区</strong><p style="color:#475569;margin-top:8px;">使用 grid-template-areas 让布局一目了然。</p></div>
    <div class="aside">Aside 160px</div>
    <div class="footer">© Footer(跨越三列)</div>
  </div>
</div>

2.2.3 gap

同 Flexbox 一样,Grid 也使用 gap 设置轨道间距。

.container {
  display: grid;
  gap: 16px;            /* 行和列间距都为 16px */
  /* row-gap: 16px; */  /* 仅行间距 */
  /* column-gap: 24px; */ /* 仅列间距 */
}

2.2.4 justify-items / align-items

属性 作用范围 效果
justify-items 所有项目在行内轴(水平)上的默认对齐方式 start / end / center / stretch
align-items 所有项目在块轴(垂直)上的默认对齐方式 start / end / center / stretch
.container {
  justify-items: center;  /* 所有项目水平居中 */
  align-items: center;    /* 所有项目垂直居中 */
}

2.2.5 justify-content / align-content

当网格的总大小小于容器时,用于控制整个网格在容器内的对齐。

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

可选值:start、end、center、stretch、space-between、space-around、space-evenly。

2.3 项目属性

2.3.1 grid-column / grid-row

控制项目在网格中的位置和跨度。

/* 使用行号 */
.item {
  grid-column: 1 / 3;    /* 从第 1 列线到第 3 列线(跨 2 列) */
  grid-row: 1 / 2;       /* 从第 1 行线到第 2 行线(跨 1 行) */
}

/* 使用 span 关键字(推荐,更直观) */
.item {
  grid-column: 1 / span 2;   /* 从第 1 列开始,跨越 2 列 */
  grid-row: 1 / span 1;      /* 从第 1 行开始,跨越 1 行 */
}

/* 简写 */
.item {
  grid-column: span 2;   /* 不指定起点,自动放置并跨 2 列 */
}

2.3.2 grid-area

可以用作 grid-row-start / grid-column-start / grid-row-end / grid-column-end 的简写,也可以引用 grid-template-areas 定义的名称。

/* 简写形式:row-start / column-start / row-end / column-end */
.item {
  grid-area: 1 / 2 / 3 / 4;  /* 行1线到行3线,列2线到列4线 */
}

/* 引用命名区域 */
.item {
  grid-area: header;
}

2.3.3 justify-self / align-self

覆盖容器上 justify-items 和 align-items 的单个项目设置。

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

2.4 关键单位与函数

2.4.1 fr 单位

fr(fraction)是 Grid 特有的弹性单位,代表可用空间的一个分数。

/* 2:1 比例的两列布局 */
.container {
  display: grid;
  grid-template-columns: 2fr 1fr;  /* 左列占 2/3,右列占 1/3 */
}

fr 与百分比的关键区别:fr 在 gap 和 fixed 轨道分配后才计算,不会溢出。

/* 百分比方式:100% + gap 会导致溢出 */
grid-template-columns: 50% 50%;  /* 如果 gap:16px,会溢出 */

/* fr 方式:自动扣除 gap 的空间 */
grid-template-columns: 1fr 1fr;  /* 100% - 16px 后等分,安全 */

2.4.2 repeat() 函数

repeat() 简化重复轨道定义,支持多种模式。

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

/* 重复多种模式 */
.container {
  grid-template-columns: repeat(2, 200px 1fr);  /* 200px 1fr 200px 1fr */
}

2.4.3 minmax() 函数

minmax(min, max) 设置轨道的最小值和最大值,是实现响应式的关键。

.container {
  grid-template-columns: repeat(3, minmax(200px, 1fr));
  /* 每列最小 200px,最大 1fr 弹性 */
}

2.4.4 auto-fill vs auto-fit

这是最令人困惑又最强大的一对属性。

/* auto-fill: 尽可能多地创建轨道(即使为空) */
.container {
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}

/* auto-fit: 收缩空轨道为 0,让有内容的项目扩展 */
.container {
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

关键区别:当项目数量不足以填满一行时:

  • auto-fill 保留空轨道的位置,整体布局不变
  • auto-fit 将空轨道折叠,剩余项目拉伸填满容器

2.4.5 fit-content()

fit-content(limit) 使轨道的大小不超过指定值,但可以小于该值(根据内容)。

.container {
  grid-template-columns: fit-content(300px) 1fr;
  /* 左侧列最大 300px,但会根据内容自适应变小 */
}

2.4.6 clamp()

clamp(MIN, PREFERRED, MAX) 不是 Grid 专属,但与响应式布局结合非常好。

font-size: clamp(1rem, 2.5vw, 1.5rem);
/* 最小 1rem,首选 2.5vw,最大 1.5rem */

在 Grid 中:

.container {
  grid-template-columns: repeat(auto-fit, minmax(clamp(150px, 20vw, 300px), 1fr));
}

2.5 命名网格线与命名区域

除了数字索引,网格线还可以有名称。

.container {
  display: grid;
  grid-template-columns: [sidebar-start] 200px [sidebar-end main-start] 1fr [main-end];
  grid-template-rows: [header] auto [content] 1fr [footer] auto;
}

.sidebar {
  grid-column: sidebar-start / sidebar-end;
  grid-row: header / footer;
}

.main {
  grid-column: main-start / main-end;
  grid-row: content;
}

repeat() 中的自动命名:

.container {
  grid-template-columns: repeat(4, [col] 1fr);
  /* 自动生成: col col col col (实际上是 col-1, col-2, col-3, col-4) */
}

2.6 隐式网格

当你放置的项目超出定义的显式网格范围时,Grid 会自动创建隐式轨道。

.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);   /* 显式:3 列 */
  grid-template-rows: 100px 100px;          /* 显式:2 行 */

  /* 隐式轨道的尺寸控制 */
  grid-auto-rows: 80px;                     /* 隐式行高度 80px */
  grid-auto-columns: 1fr;                   /* 隐式列宽度 1fr */
  grid-auto-flow: dense;                    /* 自动填充空白(密集模式) */
}

grid-auto-flow 的三个值:

值 行为
row(默认) 按行填充,先填满第一行再填下一行
column 按列填充,先填满第一列再填下一列
dense 尝试填充前面留下的空白(可能改变 DOM 顺序)

dense 的警示:使用 dense 可能会使项目的视觉顺序与 DOM 顺序不一致,影响 Tab 导航和屏幕阅读器顺序。在无障碍要求高的场景中谨慎使用。

2.7 subgrid

subgrid 让子网格继承父网格的轨道定义,而不是重新计算自己的轨道。目前浏览器支持有限(Firefox 71+、Chrome 117+、Safari 16+)。

.parent {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
}

.child {
  display: grid;
  grid-column: span 4;
  grid-template-columns: subgrid;  /* 继承父级的 4 列 */
}

用途:当父网格和子网格需要对齐时,subgrid 能确保轨道完全一致。


第三章:Flexbox vs Grid 对比

3.1 核心区别

维度 Flexbox Grid
维度 一维(行或列) 二维(行和列同时)
适用场景 组件级布局、工具栏、导航 页面级布局、复杂网格
内容驱动 是(项目大小决定排列) 否(轨道定义决定排列)
重叠 不支持 支持(同一单元格多个项目)
对齐能力 主轴 + 交叉轴 行轴 + 块轴,更精细
响应式 flex-wrap + flex-basis auto-fit/auto-fill + minmax

3.2 何时用 Flexbox

✅ 一维排列:导航栏、标签页、按钮组、工具条
✅ 内容大小决定布局:卡片底部对齐、头像+文字组合
✅ 简单居中:垂直居中、水平居中
✅ 组件内部:按钮内的图标+文字、input 前置图标

/* 导航栏——Flexbox 的天然场景 */
.nav {
  display: flex;
  gap: 8px;
  align-items: center;
}
.nav .spacer {
  margin-left: auto;  /* 推到右边 */
}

3.3 何时用 Grid

✅ 二维布局:页面整体结构、仪表盘、照片墙
✅ 行列对齐:表格状数据展示、产品列表、卡片网格
✅ 显式行+列控制:圣杯布局、侧边栏+页头的复杂结构
✅ 项目重叠:装饰层叠效果、封面图+标题叠加

/* 页面布局——Grid 的强项 */
.page {
  display: grid;
  grid-template-columns: 220px 1fr;
  grid-template-rows: 60px 1fr 40px;
  grid-template-areas:
    "header header"
    "sidebar main"
    "footer footer";
  height: 100vh;
}

3.4 混合使用的黄金法则

页面用 Grid,组件用 Flexbox。Grid 负责宏观,Flexbox 负责微观。


第四章:经典布局实战

4.1 响应式导航栏(Flexbox)

4.2 卡片网格(Grid auto-fill)

4.3 圣杯布局(Grid template areas)

4.4 侧边栏 + 主体(Grid + Flexbox 混合)

4.5 仪表盘布局(命名区域 + 隐式网格)


第五章:性能与最佳实践

5.1 性能考虑

  1. Grid 比 Flexbox 略重:Grid 的二维布局计算比 Flexbox 的一维计算更复杂。在包含数千个项目的超大列表中,Flexbox 的渲染性能略优。

  2. 避免深层嵌套:无论使用 Flexbox 还是 Grid,过深的嵌套都会增加浏览器的 Reflow 开销。尽量保持布局层级的扁平化。

  3. gap 优于 margin:使用 gap 而不是 margin 控制间距,可以减少元素数量的计算复杂度。

  4. grid-auto-flow: dense 有代价:密集填充模式需要额外的布局计算,在超大网格中可能有性能开销。

5.2 无障碍性

  1. 不要只依赖视觉顺序:order 和 grid-auto-flow: dense 会改变视觉顺序而不改变 DOM 顺序,可能导致屏幕阅读器用户的体验混乱。

  2. Grid 区域的名称为语义化标签:使用 <header>、<main>、<aside>、<footer> 等语义化 HTML 标签,而不是通用的 <div>,并结合 grid-area 使用。

  3. 保持逻辑焦点顺序:确保 Tab 键的焦点顺序与页面的视觉阅读顺序一致。

5.3 浏览器兼容性

特性 Chrome Firefox Safari Edge
Flexbox ✅ 全支持 ✅ 全支持 ✅ 全支持 ✅ 全支持
Grid ✅ 57+ ✅ 52+ ✅ 10.1+ ✅ 16+
gap in Flex ✅ 84+ ✅ 63+ ✅ 14.1+ ✅ 84+
subgrid ✅ 117+ ✅ 71+ ✅ 16+ ✅ 117+
auto-fill / auto-fit ✅ 57+ ✅ 52+ ✅ 10.1+ ✅ 16+

总结

CSS Flexbox 和 Grid 是现代 Web 布局的两大基石。简单来说:

  • Flexbox = 一维、内容驱动、适合组件内部布局
  • Grid = 二维、容器驱动、适合页面整体布局
  • 最佳实践 = 页面用 Grid,组件用 Flexbox,混合使用发挥最大威力

掌握这两种布局方式,意味着你不再需要 float、inline-block、clearfix 这些历史上"勉强可用"的布局方案。你会发现,你思考布局的方式从"怎么把元素放到正确位置"变成了"我想要什么样的布局效果",这正是 CSS 现代布局革命带给我们的自由。


本文共约 11,000 字,发布于 2024-08-27