CSS 布局终极指南:Flex 和 Grid 一次搞懂

彻底掌握 CSS Flexbox 和 Grid 两大布局神器,从基础概念到实战案例,让你不再为布局发愁。

13 分钟阅读
小明

CSS 布局终极指南:Flex 和 Grid 一次搞懂

「这个元素怎么居中?」 「为什么左边的元素跑到右边去了?」 「两栏布局怎么做来着?」

CSS 布局曾经是前端开发者的噩梦。float、position、负 margin、clearfix……各种 hack 满天飞。

直到 FlexboxGrid 的出现,布局终于变得简单了。

今天小明带你彻底搞懂这两个布局神器,从此告别布局焦虑。


Flexbox:一维布局之王

什么是 Flexbox?

Flexbox(弹性盒子)是一种一维布局方式,擅长处理单行或单列的元素排列。

.container {
  display: flex;  /* 开启 flex 布局 */
}

开启后,容器变成 flex 容器,里面的子元素变成 flex 项目

┌─────────────────────────────────────────┐
│           flex 容器 (container)          │
│  ┌───────┐  ┌───────┐  ┌───────┐       │
│  │ item1 │  │ item2 │  │ item3 │       │
│  └───────┘  └───────┘  └───────┘       │
│                                         │
│  ◀─────── 主轴 (main axis) ──────────▶  │
└─────────────────────────────────────────┘
            ▲
            │ 交叉轴 (cross axis)
            ▼

主轴与交叉轴

Flexbox 的核心概念是

  • 主轴(main axis):项目排列的方向
  • 交叉轴(cross axis):垂直于主轴的方向
/* 主轴方向 */
.container {
  flex-direction: row;           /* 默认,水平从左到右 → */
  flex-direction: row-reverse;   /* 水平从右到左 ← */
  flex-direction: column;        /* 垂直从上到下 ↓ */
  flex-direction: column-reverse; /* 垂直从下到上 ↑ */
}

容器属性

justify-content:主轴对齐

.container {
  display: flex;
  justify-content: flex-start;    /* 起点对齐(默认)*/
  justify-content: flex-end;      /* 终点对齐 */
  justify-content: center;        /* 居中 */
  justify-content: space-between; /* 两端对齐,间距相等 */
  justify-content: space-around;  /* 每个项目两侧间距相等 */
  justify-content: space-evenly;  /* 所有间距相等 */
}

图示:

flex-start:      [A][B][C]              
flex-end:                    [A][B][C]
center:              [A][B][C]
space-between:  [A]      [B]      [C]
space-around:    [A]    [B]    [C]  
space-evenly:   [A]   [B]   [C]

align-items:交叉轴对齐

.container {
  display: flex;
  align-items: stretch;     /* 拉伸填满(默认)*/
  align-items: flex-start;  /* 起点对齐 */
  align-items: flex-end;    /* 终点对齐 */
  align-items: center;      /* 居中 */
  align-items: baseline;    /* 基线对齐 */
}

flex-wrap:换行

.container {
  display: flex;
  flex-wrap: nowrap;       /* 不换行(默认)*/
  flex-wrap: wrap;         /* 换行 */
  flex-wrap: wrap-reverse; /* 换行,但反向 */
}

gap:间距

.container {
  display: flex;
  gap: 20px;          /* 所有间距 */
  gap: 10px 20px;     /* 行间距 列间距 */
  row-gap: 10px;      /* 行间距 */
  column-gap: 20px;   /* 列间距 */
}

项目属性

flex-grow:放大比例

当容器有剩余空间时,项目如何瓜分。

.item1 { flex-grow: 1; }  /* 占 1 份 */
.item2 { flex-grow: 2; }  /* 占 2 份 */
.item3 { flex-grow: 1; }  /* 占 1 份 */

/* 剩余空间被分成 1+2+1=4 份 */
/* item1 得 1/4,item2 得 2/4,item3 得 1/4 */

flex-shrink:缩小比例

当容器空间不足时,项目如何收缩。

.item { flex-shrink: 0; }  /* 不收缩 */
.item { flex-shrink: 1; }  /* 按比例收缩(默认)*/

flex-basis:初始大小

.item { flex-basis: 200px; }  /* 初始宽度 200px */
.item { flex-basis: 30%; }    /* 初始宽度 30% */
.item { flex-basis: auto; }   /* 根据内容决定(默认)*/

flex 简写

/* flex: grow shrink basis */
.item { flex: 0 1 auto; }  /* 默认值 */
.item { flex: 1; }         /* 等于 flex: 1 1 0% */
.item { flex: auto; }      /* 等于 flex: 1 1 auto */
.item { flex: none; }      /* 等于 flex: 0 0 auto */

align-self:单独对齐

.item {
  align-self: auto;       /* 继承容器的 align-items */
  align-self: flex-start;
  align-self: flex-end;
  align-self: center;
  align-self: stretch;
}

Flexbox 实战案例

案例 1:完美居中

.container {
  display: flex;
  justify-content: center;  /* 水平居中 */
  align-items: center;      /* 垂直居中 */
  height: 100vh;
}

这是最简单的居中方式,告别 position: absolute + transform

案例 2:导航栏

<nav class="navbar">
  <div class="logo">Logo</div>
  <ul class="nav-links">
    <li>首页</li>
    <li>产品</li>
    <li>关于</li>
  </ul>
  <button class="login-btn">登录</button>
</nav>
.navbar {
  display: flex;
  justify-content: space-between;  /* logo 左边,按钮右边 */
  align-items: center;
  padding: 0 20px;
  height: 60px;
}

.nav-links {
  display: flex;
  gap: 20px;
  list-style: none;
}

案例 3:卡片列表自适应

.card-list {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
}

.card {
  flex: 1 1 300px;  /* 最小 300px,可放大,可换行 */
  max-width: 400px;
}

案例 4:底部固定的页脚

body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

main {
  flex: 1;  /* 主内容区占据剩余空间 */
}

footer {
  /* 自动被推到底部 */
}

Grid:二维布局之王

什么是 Grid?

Grid 是一种二维布局方式,可以同时处理行和列。

.container {
  display: grid;
}

如果说 Flexbox 是「一维的」,Grid 就是「二维的」:

Flexbox(一维):
┌───┐┌───┐┌───┐
│ 1 ││ 2 ││ 3 │
└───┘└───┘└───┘

Grid(二维):
┌───┬───┬───┐
│ 1 │ 2 │ 3 │
├───┼───┼───┤
│ 4 │ 5 │ 6 │
├───┼───┼───┤
│ 7 │ 8 │ 9 │
└───┴───┴───┘

定义网格

grid-template-columns:列

.container {
  display: grid;
  
  /* 三列,每列 200px */
  grid-template-columns: 200px 200px 200px;
  
  /* 三列,等分 */
  grid-template-columns: 1fr 1fr 1fr;
  
  /* 简写 */
  grid-template-columns: repeat(3, 1fr);
  
  /* 混合 */
  grid-template-columns: 200px 1fr 2fr;
  
  /* 自适应列数 */
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

grid-template-rows:行

.container {
  display: grid;
  grid-template-rows: 100px 200px 100px;
  grid-template-rows: 1fr 2fr 1fr;
  grid-template-rows: repeat(3, 100px);
}

gap:间距

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

fr 单位

fr 是 Grid 的特殊单位,表示可用空间的份数

.container {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;
  /* 第一列占 1/4,第二列占 2/4,第三列占 1/4 */
}

项目定位

自动放置

默认情况下,项目按顺序放入网格。

手动定位

.item {
  grid-column-start: 1;   /* 从第 1 列线开始 */
  grid-column-end: 3;     /* 到第 3 列线结束 */
  grid-row-start: 1;
  grid-row-end: 2;
  
  /* 简写 */
  grid-column: 1 / 3;     /* 跨越第 1-2 列 */
  grid-row: 1 / 2;
  
  /* 更简写 */
  grid-area: 1 / 1 / 2 / 3;  /* row-start / col-start / row-end / col-end */
  
  /* 用 span 表示跨越 */
  grid-column: 1 / span 2;  /* 从第 1 列开始,跨 2 列 */
}

网格线编号

列线: 1     2     3     4
      │     │     │     │
      ▼     ▼     ▼     ▼
      ┌─────┬─────┬─────┐ ← 行线 1
      │  1  │  2  │  3  │
      ├─────┼─────┼─────┤ ← 行线 2
      │  4  │  5  │  6  │
      └─────┴─────┴─────┘ ← 行线 3

区域命名

可以给网格区域起名字,让布局更直观。

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

对齐方式

justify-items / align-items:项目在格子内的对齐

.container {
  display: grid;
  justify-items: start | end | center | stretch;
  align-items: start | end | center | stretch;
  
  /* 简写 */
  place-items: center center;
}

justify-content / align-content:整个网格在容器内的对齐

.container {
  display: grid;
  justify-content: start | end | center | space-between | space-around;
  align-content: start | end | center | space-between | space-around;
  
  /* 简写 */
  place-content: center center;
}

Grid 实战案例

案例 1:经典三栏布局

.layout {
  display: grid;
  grid-template-columns: 200px 1fr 200px;
  grid-template-rows: 60px 1fr 60px;
  min-height: 100vh;
  gap: 10px;
}

.header { grid-column: 1 / -1; }  /* 跨越所有列 */
.footer { grid-column: 1 / -1; }

案例 2:响应式图片画廊

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

.gallery img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
}

auto-fill + minmax 是响应式布局的黄金组合:

  • 最小 250px
  • 最大 1fr(等分剩余空间)
  • 自动决定每行放几个

案例 3:仪表盘布局

.dashboard {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(3, 200px);
  gap: 20px;
}

/* 大卡片跨越多个格子 */
.card-large {
  grid-column: span 2;
  grid-row: span 2;
}

.card-wide {
  grid-column: span 2;
}

.card-tall {
  grid-row: span 2;
}

案例 4:圣杯布局

.holy-grail {
  display: grid;
  grid-template-areas:
    "header header header"
    "nav    main   aside"
    "footer footer footer";
  grid-template-columns: 200px 1fr 200px;
  grid-template-rows: auto 1fr auto;
  min-height: 100vh;
}

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

Flexbox vs Grid:什么时候用哪个?

场景推荐原因
导航栏Flexbox一维排列
卡片列表两者皆可Flexbox 更简单
页面整体布局Grid二维控制更方便
项目居中Flexbox代码更少
复杂仪表盘Grid可精确控制位置
表单布局Grid对齐更方便

简单原则

  • 一维排列 → Flexbox
  • 二维布局 → Grid
  • 不确定 → 先试 Flexbox,不行再换 Grid

它们也可以组合使用

/* Grid 做页面布局 */
.page {
  display: grid;
  grid-template-columns: 200px 1fr;
}

/* Flexbox 做组件内部布局 */
.card {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

常用布局速查表

水平居中

/* 块级元素 */
.center { margin: 0 auto; }

/* Flex */
.parent { display: flex; justify-content: center; }

/* Grid */
.parent { display: grid; place-items: center; }

垂直居中

/* Flex */
.parent {
  display: flex;
  align-items: center;
  min-height: 100vh;
}

/* Grid */
.parent {
  display: grid;
  place-items: center;
  min-height: 100vh;
}

两栏布局(左固定右自适应)

/* Flex */
.container {
  display: flex;
}
.left { width: 200px; }
.right { flex: 1; }

/* Grid */
.container {
  display: grid;
  grid-template-columns: 200px 1fr;
}

等高列

/* Flex 默认就是等高 */
.container {
  display: flex;
}

/* Grid 也是 */
.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

底部固定

body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}
main { flex: 1; }

总结

Flexbox 核心:

  • display: flex 开启
  • flex-direction 控制方向
  • justify-content 主轴对齐
  • align-items 交叉轴对齐
  • flex: 1 让项目填满剩余空间

Grid 核心:

  • display: grid 开启
  • grid-template-columns/rows 定义网格
  • fr 单位表示份数
  • gap 设置间距
  • grid-area 命名区域

选择原则

  • 一维用 Flex,二维用 Grid
  • 简单场景用 Flex,复杂场景用 Grid
  • 两者可以组合使用

掌握这两个布局工具,你就能轻松应对 90% 的布局需求。


小明冷笑话收尾:

问:CSS 布局最难的是什么? 答:垂直居中。

问:有了 Flexbox 之后呢? 答:记住 display: flex; justify-content: center; align-items: center;

这三行代码价值一万块,因为它能让你少掉一万根头发。

「布局不难,难的是记住这些属性名。」—— 小明