CSS Flexbox 与 Grid:何时使用哪个

· 12分钟阅读

目录

在 CSS Flexbox 和 Grid 之间做选择可能让人感到不知所措,尤其是当两者似乎都能解决相同的布局问题时。事实上,它们是为不同目的而设计的,理解它们的优势将使你成为更高效的开发者。

本指南详细介绍了关于 Flexbox 和 Grid 你需要知道的一切,从核心概念到实际应用。读完后,你将确切知道在任何布局场景中该使用哪个工具。

Flexbox 基础(一维布局)

Flexbox 专为一维布局设计——在单行或单列中排列项目。它擅长沿一个轴分配空间和对齐项目,非常适合导航栏、按钮组和单向流动的内容。

核心概念

每个 Flexbox 布局都有一个弹性容器(父元素)和弹性项目(子元素)。容器控制其项目的方向、换行和对齐:

/* 弹性容器 */
.container {
  display: flex;
  flex-direction: row;      /* row | row-reverse | column | column-reverse */
  justify-content: center;  /* 主轴对齐 */
  align-items: center;      /* 交叉轴对齐 */
  gap: 1rem;                /* 项目之间的间距 */
  flex-wrap: wrap;          /* 允许项目换行 */
}

/* 弹性项目 */
.item {
  flex: 1;                  /* 增长以填充可用空间 */
  /* 简写形式: flex-grow: 1; flex-shrink: 1; flex-basis: 0; */
}

.item-fixed {
  flex: 0 0 200px;          /* 不增长,不收缩,200px 宽 */
}

理解主轴和交叉轴

掌握 Flexbox 的关键是理解它的两个轴。主轴沿着 flex-direction 设置的方向运行(row 为水平,column 为垂直)。交叉轴垂直于主轴。

这个轴系统决定了哪些属性控制哪些维度。justify-content 始终作用于主轴,而 align-items 作用于交叉轴。

对齐属性

/* 主轴(row 方向为水平) */
.container {
  justify-content: flex-start;   /* 默认:项目靠起点对齐 */
  justify-content: flex-end;     /* 项目靠终点对齐 */
  justify-content: center;       /* 项目居中 */
  justify-content: space-between;/* 项目之间等距 */
  justify-content: space-around; /* 项目周围等距 */
  justify-content: space-evenly; /* 所有地方等距 */
}

/* 交叉轴(row 方向为垂直) */
.container {
  align-items: stretch;    /* 默认:拉伸填充容器 */
  align-items: flex-start; /* 顶部对齐 */
  align-items: flex-end;   /* 底部对齐 */
  align-items: center;     /* 垂直居中 */
  align-items: baseline;   /* 文本基线对齐 */
}

Flex 属性详解

flex 属性是三个属性的简写:flex-growflex-shrinkflex-basis。单独理解这些属性有助于你编写更精确的布局。

.item-grow {
  flex: 1;        /* 等同于: 1 1 0% */
}

.item-fixed {
  flex: 0 0 200px; /* 不增长或收缩,保持 200px */
}

.item-shrink {
  flex: 0 1 auto;  /* 不增长,但需要时可以收缩 */
}

专业提示: 当你希望项目平均分配空间时使用 flex: 1,当你希望项目保持其自然大小而不增长或收缩时使用 flex: 0 0 auto

Grid 基础(二维布局)

CSS Grid 专为二维布局而构建——同时控制行和列。它是页面级布局、卡片网格以及任何需要精确控制两个维度的设计的首选。

核心概念

Grid 布局由网格容器网格项目组成。与 Flexbox 不同,Grid 允许你定义显式轨道(行和列)并将项目放置在该结构中的任何位置:

/* 网格容器 */
.container {
  display: grid;
  grid-template-columns: 200px 1fr 1fr;  /* 3列 */
  grid-template-rows: auto 1fr auto;     /* 3行 */
  gap: 1rem;                             /* 单元格之间的间距 */
  grid-auto-flow: row;                   /* 自动放置项目的流动方式 */
}

/* 网格项目 */
.item {
  grid-column: 1 / 3;    /* 从列线1跨越到3 */
  grid-row: 1 / 2;       /* 占据第一行 */
}

.item-area {
  grid-area: header;     /* 放置在命名区域 */
}

理解网格线和轨道

Grid 在轨道之间创建编号线。一个3列网格有4条垂直线(1、2、3、4)。项目通过指定它们跨越的线来放置。

轨道是线之间的空间——你实际的列和行。你可以使用固定单位(px)、弹性单位(fr)或基于内容的大小(auto、min-content、max-content)来调整它们的大小。

FR 单位

fr 单位是 Grid 的超能力。它代表可用空间的一部分,按比例在轨道之间分配:

.container {
  grid-template-columns: 1fr 2fr 1fr;  /* 中间列获得2倍空间 */
  grid-template-columns: 200px 1fr;    /* 固定侧边栏,弹性内容 */
  grid-template-columns: repeat(3, 1fr); /* 三个相等的列 */
}

命名网格区域

Grid 的模板区域功能允许你使用类似 ASCII 艺术的语法创建布局,使复杂布局易于阅读:

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

.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.content { grid-area: content; }
.footer { grid-area: footer; }

自动放置和网格自动流

Grid 可以自动放置你没有明确定位的项目。grid-auto-flow 属性控制项目是先填充行(默认)还是先填充列:

.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-flow: dense;  /* 用较小的项目填充空隙 */
}

专业提示: 对于类似瀑布流的布局,使用 grid-auto-flow: dense 来填充空隙,但要注意这可能会改变项目的视觉顺序,可能影响可访问性。

并排比较

让我们在关键维度上比较 Flexbox 和 Grid,以理解它们的根本区别:

特性 Flexbox Grid
维度 一维(行或列) 二维(行和列)
内容驱动 是 - 项目控制其大小 否 - 容器定义结构
布局方向 一次一个轴 同时两个轴
项目放置 顺序(源顺序) 可以显式定位
对齐 单轴上强大 两个轴上都强大
间距支持 是(现代浏览器) 是(从一开始)
最适合 组件、导航、工具栏 页面布局、卡片网格、仪表板
学习曲线 中等 初期较陡

概念差异

根本区别在于控制。Flexbox 是由内而外——项目根据其内容大小影响布局。Grid 是由外而内——你首先定义结构,然后将内容放入其中。

把 Flexbox 想象成一把灵活的尺子,可以根据你测量的内容进行调整。Grid 更像是方格纸,你预先决定结构。

场景 Flexbox 方法 Grid 方法
导航栏 自然适配 - 项目排成一行 过度 - 结构太多
卡片网格 可能但换行笨拙 完美 - 显式的行和列
表单布局 适合简单表单 更适合复杂的多列表单
页面布局 需要嵌套多个容器 单个容器就能处理
内容居中 出色 - 简单直观 同样出色 - place-items: center

何时使用 Flexbox

Flexbox 在内容沿单一方向流动且项目需要适应可用空间的场景中表现出色。以下是 Flexbox 是最佳选择的情况:

导航栏和菜单

导航组件是 Flexbox 的典型应用。项目自然地排成一行,你通常希望它们均匀分配空间或对齐到相对的两端:

.navbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem;
}

.nav-links {
  display: flex;
  gap: 2rem;
  list-style: none;
}

按钮组和工具栏

当你有一组应该并排放置的按钮或工具时,Flexbox 完美地处理间距和对齐:

.button-group {
  display: flex;
  gap: 0.5rem;
}

.toolbar {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 1rem;
}

媒体对象

经典的媒体对象模式(一侧是图像,另一侧是内容)用 Flexbox 非常简单:

.media {
  display: flex;
  gap: 1rem;
}

.media-image {
  flex: 0 0 100px;  /* 固定宽度图像 */
}

.media-content {
  flex: 1;  /* 内容占据剩余空间 */
}

表单控件

标签、输入框和按钮排成一行的输入组非常适合 Flexbox:

.input-group {
  display: flex;
  gap: 0.5rem;
}

.input-group input {
  flex: 1;  /* 输入框增长以填充空间 */
}

.input-group button {
  flex: 0 0 auto;  /* 按钮保持自然大小 */
}

垂直居中