跳到主要内容

Flex 布局

问题

Flex 布局的核心概念是什么?容器属性和项目属性分别有哪些?如何用 Flex 实现常见布局?

答案

Flex 布局概述

Flex(Flexible Box,弹性盒子)是 CSS3 中专门为一维布局设计的模型。它可以轻松实现元素的对齐、排列、分配空间,极大简化了传统浮动和定位布局的复杂性。

启用 Flex 布局
.container {
display: flex; /* 块级 Flex 容器 */
/* 或 */
display: inline-flex; /* 行内 Flex 容器 */
}
Flex vs Grid
  • Flex:一维布局(一行或一列),适合组件内部的排列
  • Grid:二维布局(行和列同时控制),适合整体页面布局

详见 Grid 布局

核心概念

主轴 (Main Axis) →
┌──────────────────────────────────────┐
│ ┌─────┐ ┌─────┐ ┌─────┐ │ ↕ 交叉轴
│ │ 1 │ │ 2 │ │ 3 │ │ (Cross Axis)
│ └─────┘ └─────┘ └─────┘ │
└──────────────────────────────────────┘
Flex Container(容器)
└── Flex Items(项目)
概念说明
Flex Container设置了 display: flex 的父元素
Flex Item容器的直接子元素
主轴(Main Axis)Flex 项目的排列方向,默认水平 →
交叉轴(Cross Axis)与主轴垂直的方向,默认垂直 ↓
main-start / main-end主轴的起点和终点
cross-start / cross-end交叉轴的起点和终点

容器属性(Container Properties)

1. flex-direction — 主轴方向

.container {
flex-direction: row; /* 默认:水平从左到右 → */
flex-direction: row-reverse; /* 水平从右到左 ← */
flex-direction: column; /* 垂直从上到下 ↓ */
flex-direction: column-reverse; /* 垂直从下到上 ↑ */
}

2. flex-wrap — 换行方式

.container {
flex-wrap: nowrap; /* 默认:不换行(项目可能压缩) */
flex-wrap: wrap; /* 换行,从上到下排列 */
flex-wrap: wrap-reverse; /* 换行,从下到上排列 */
}

3. flex-flow — 简写

.container {
flex-flow: row wrap; /* = flex-direction: row + flex-wrap: wrap */
}

4. justify-content — 主轴对齐

.container {
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:     |■ ■ ■          |
flex-end: | ■ ■ ■|
center: | ■ ■ ■ |
space-between: |■ ■ ■|
space-around: | ■ ■ ■ |
space-evenly: | ■ ■ ■ |
space-between vs space-around vs space-evenly
  • space-between:首尾项目贴边,中间等间距。间距 = 剩余空间 / (n-1)
  • space-around:每个项目两侧间距相等 → 首尾项目到边缘的间距是中间间距的 一半
  • space-evenly:所有间距(含到边缘)完全相等

5. align-items — 交叉轴对齐(单行)

.container {
align-items: stretch; /* 默认:拉伸填满容器高度 */
align-items: flex-start; /* 靠交叉轴起点 */
align-items: flex-end; /* 靠交叉轴终点 */
align-items: center; /* 居中 */
align-items: baseline; /* 按文字基线对齐 */
}

6. align-content — 交叉轴对齐(多行)

当有多行时(flex-wrap: wrap),align-content 控制行与行之间的间距,取值与 justify-content 类似。

.container {
flex-wrap: wrap;
align-content: flex-start; /* 靠顶部 */
align-content: flex-end; /* 靠底部 */
align-content: center; /* 居中 */
align-content: space-between; /* 首尾行贴边 */
align-content: space-around; /* 均匀分布 */
align-content: stretch; /* 默认:行拉伸填满 */
}
align-items vs align-content
  • align-items:控制单行内项目在交叉轴上的对齐
  • align-content:控制多行之间在交叉轴上的分布
  • 只有一行时 align-content 无效

7. gap — 间距

.container {
gap: 16px; /* 行间距和列间距都是 16px */
gap: 16px 24px; /* 行间距 16px,列间距 24px */
row-gap: 16px; /* 仅行间距 */
column-gap: 24px; /* 仅列间距 */
}
gap vs margin

gap 只作用于项目之间,不影响首尾项目到容器的距离。比起每个项目设 margin 再处理首尾特殊情况,gap 更简洁。

项目属性(Item Properties)

1. flex-grow — 放大比例

定义项目的放大比例,分配剩余空间。默认值 0(不放大)。

.item-a { flex-grow: 1; } /* 占 1 份 */
.item-b { flex-grow: 2; } /* 占 2 份 */
.item-c { flex-grow: 1; } /* 占 1 份 */
/* A 和 C 各占剩余空间的 1/4,B 占 2/4 */

2. flex-shrink — 缩小比例

定义项目的缩小比例,当空间不足时如何分配压缩量。默认值 1(等比缩小)。

.item-a { flex-shrink: 0; } /* 不缩小 */
.item-b { flex-shrink: 1; } /* 默认:参与缩小 */
.item-c { flex-shrink: 2; } /* 缩小量是 B 的 2 倍 */
flex-shrink 的计算

缩小量的分配与 flex-shrink 值和项目自身宽度的乘积成正比,而非简单的比例关系。

假设容器宽 300px,A(150px, shrink:1)、B(200px, shrink:2),溢出 50px:

  • A 的权重 = 150 × 1 = 150
  • B 的权重 = 200 × 2 = 400
  • A 缩小 = 50 × 150/(150+400) ≈ 13.6px
  • B 缩小 = 50 × 400/(150+400) ≈ 36.4px

3. flex-basis — 初始尺寸

定义项目在分配剩余空间之前的基础尺寸。默认值 auto(即项目自身的 width/height)。

.item {
flex-basis: 200px; /* 初始宽度 200px */
flex-basis: 30%; /* 初始宽度为容器的 30% */
flex-basis: auto; /* 默认:使用 width 属性 */
flex-basis: 0; /* 不考虑自身尺寸,完全由 flex-grow 分配 */
}
flex-basis vs width

flex-basiswidth 同时设置时,flex-basis 优先级更高flex-direction: column 时对应 height)。但 flex-basismin-widthmax-width 约束。

4. flex — 简写(重要)

.item {
flex: <grow> <shrink> <basis>;
}

常用简写值:

写法等价于说明
flex: 1flex: 1 1 0%等分剩余空间
flex: autoflex: 1 1 auto基于内容大小弹性伸缩
flex: noneflex: 0 0 auto不伸不缩,固定大小
flex: 0flex: 0 1 0%不放大,可缩小
flex: 2flex: 2 1 0%占 2 份空间
flex: 1 vs flex: auto

这是高频面试题!关键区别在于 flex-basis

  • flex: 1flex-basis: 0% → 不考虑内容大小,完全按比例分配
  • flex: autoflex-basis: auto → 先按内容大小分配,剩余空间再按比例分配
/* 三个项目,内容长度不同 */
.equal > * { flex: 1; } /* 三个项目宽度完全相等 */
.auto > * { flex: auto; } /* 内容多的项目更宽 */

5. align-self — 单独对齐

覆盖容器的 align-items,让某个项目单独在交叉轴上对齐。

.container { align-items: flex-start; }
.special-item {
align-self: center; /* 只有这个项目居中 */
}

6. order — 排列顺序

.item-a { order: 3; } /* 排第三 */
.item-b { order: 1; } /* 排第一 */
.item-c { order: 2; } /* 排第二 */
/* 默认 order: 0,值越小越靠前 */

常见布局实现

导航栏(Logo 左 + 菜单右)

导航栏布局
.navbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
height: 64px;
}

等分布局

等分布局
.grid {
display: flex;
gap: 16px;
}
.grid > .col {
flex: 1; /* 等分空间 */
}

固定侧边栏 + 自适应内容

两栏布局
.layout {
display: flex;
height: 100vh;
}
.sidebar {
flex: none; /* 不伸缩 */
width: 240px;
}
.main {
flex: 1; /* 占满剩余空间 */
overflow: auto;
}
Sticky Footer
.page {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.header { /* 固定高度 */ }
.content {
flex: 1; /* 占满中间空间 */
}
.footer { /* 固定高度,始终在底部 */ }

水平垂直居中

Flex 居中
.container {
display: flex;
justify-content: center;
align-items: center;
}

更多居中方案详见 CSS 居中方案


常见面试问题

Q1: Flex 容器的属性有哪些?

答案

6 个容器属性 + 1 个间距属性:

属性作用默认值
flex-direction主轴方向row
flex-wrap是否换行nowrap
flex-flowdirection + wrap 简写row nowrap
justify-content主轴对齐方式flex-start
align-items交叉轴对齐(单行)stretch
align-content交叉轴对齐(多行)stretch
gap项目间距0

Q2: flex: 1 是什么含义?和 flex: auto 有什么区别?

答案

写法展开flex-basis行为
flex: 11 1 0%0%忽略内容大小,完全按比例分配
flex: auto1 1 autoauto先考虑内容大小,剩余空间按比例分配
<div style="display: flex; width: 300px;">
<div style="flex: 1;"></div>
<div style="flex: 1;">很长很长的文本内容</div>
</div>
<!-- flex: 1 → 两个 div 各 150px -->

<div style="display: flex; width: 300px;">
<div style="flex: auto;"></div>
<div style="flex: auto;">很长很长的文本内容</div>
</div>
<!-- flex: auto → 长文本的 div 更宽 -->

Q3: flex-shrink 的缩小比例怎么计算?

答案

缩小量与 flex-shrink × flex-basis 的乘积成正比。

溢出量 = 所有项目总宽度 - 容器宽度
项目缩小量 = 溢出量 × (该项目的 shrink × basis) / (所有项目的 shrink × basis 之和)

例如容器 400px,三个项目 A(200px, shrink:1)、B(200px, shrink:2)、C(200px, shrink:1):

  • 溢出 = 600 - 400 = 200px
  • A 权重 = 200×1 = 200,B 权重 = 200×2 = 400,C 权重 = 200×1 = 200
  • A 缩小 = 200 × 200/800 = 50px → 最终 150px
  • B 缩小 = 200 × 400/800 = 100px → 最终 100px
  • C 缩小 = 200 × 200/800 = 50px → 最终 150px

Q4: align-itemsalign-content 的区别?

答案

特性align-itemsalign-content
作用对象每一行内的项目多行之间
生效条件始终生效仅在 flex-wrap: wrap 且有多行时
可选值stretch/start/end/center/baselinestretch/start/end/center/space-between/space-around

单行时只需关心 align-items,多行时两者配合使用。

Q5: flex-basiswidth 哪个优先级更高?

答案

当同时设置时,flex-basis 优先级更高。但都受 min-width / max-width 约束:

max-width / min-width  >  flex-basis  >  width
.item {
width: 200px;
flex-basis: 300px;
max-width: 250px;
}
/* 最终基础宽度 = min(300px, 250px) = 250px */

flex-basis: auto 时,会使用 width 的值作为基础尺寸。

Q6: 如何让 Flex 项目换行后每行保持等分?

答案

换行等分
.container {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.item {
/* 每行 3 个,减去 gap */
flex: 0 0 calc((100% - 16px * 2) / 3);
/* 或使用 width */
width: calc((100% - 16px * 2) / 3);
}

但对于网格布局场景,更推荐使用 Grid 布局

Q7: Flex 布局中文本溢出省略号为什么失效了?

答案

Flex 项目的默认 min-widthauto(即内容的最小宽度),导致项目不会被压缩到内容以下。解决方案:

.flex-item {
min-width: 0; /* 或 overflow: hidden */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

嵌套 Flex 时,每一层都需要设置 min-width: 0overflow: hidden

Q8: order 属性对可访问性有什么影响?

答案

order 只改变视觉顺序,不改变 DOM 顺序。这意味着:

  • Tab 键仍按 DOM 顺序聚焦
  • 屏幕阅读器仍按 DOM 顺序读取

如果视觉顺序和 DOM 顺序严重不一致,会导致键盘用户困惑。建议尽量通过调整 HTML 结构来控制顺序,而非过度依赖 order

Q9: flex-direction: column 时,主轴和交叉轴怎么变?

答案

主轴变为垂直方向,交叉轴变为水平方向

属性row(默认)column
主轴方向水平 →垂直 ↓
交叉轴方向垂直 ↓水平 →
justify-content 控制水平对齐垂直对齐
align-items 控制垂直对齐水平对齐
flex-basis 影响宽度高度

Q10: 如何用 Flex 实现圣杯布局?

答案

Flex 圣杯布局
.layout {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.header, .footer {
flex: none; /* 固定高度 */
}
.body {
display: flex;
flex: 1;
}
.left-sidebar {
flex: none;
width: 200px;
order: -1; /* DOM 中 main 在前,视觉上 sidebar 在左 */
}
.main {
flex: 1;
}
.right-sidebar {
flex: none;
width: 200px;
}

相关链接