# display: flex 到底改了哪些规则：主轴、交叉轴，以及 justify-content 和 align-items 各管哪一头

display: flex 改的是父元素的内层类型和子元素身份，主轴由 flex-direction 决定，justify-content 分配主轴富余空间，align-items 默认 stretch 让卡片自动等高

> CSS 基础 · 页面渲染 · 约 7 分钟 · 10 月 08 日

## 本篇要点

1. `display: flex` 等同于 `block flex`：父元素外层仍是块级（照旧独占一行），变的是内层——从常规流换成 flex 布局；`inline-flex` 则是外层行内级、内层同样 flex。
2. 只有容器的直接子元素成为 flex item，而且它们的 `display` 会被块化，所以 `span` 一旦成为 flex item 就能设宽高和上下 margin。
3. flex 容器建立 flex 格式化上下文：子元素的 `float`、`clear` 不再生效，`vertical-align` 对 flex item 无效，相邻 flex item 的外边距不再合并。
4. 主轴由 `flex-direction` 指定（默认 `row`，沿文字方向），交叉轴是与主轴垂直的那条；判断时先找主轴，剩下那条就是交叉轴，不要按“横竖”记。
5. 记忆规则是 `justify-` 管主轴、`align-` 管交叉轴：默认 `row` 时 `justify-content` 管左右、`align-items` 管上下；改成 `column` 后两者管的方向整体对调。
6. 子项在主轴方向默认保持自己的尺寸、不会自动填满容器，所以主轴才留下富余空间；空间不足时则被压缩而不是溢出。
7. `justify-content` 分配的就是主轴上的富余空间，因此没有富余空间时几个取值看起来一样；`flex-direction: column` 下容器高度由内容撑开，不设 height 就看不到效果。
8. `align-items` 的初始值是 `stretch`，一行卡片自动等高正是它造成的；改成 `center` 等值后子项回到内容高度，再谈居中。交叉轴的富余空间通常来自这一行里最高的元素，所以居中不必先给容器设高度。
9. flex 里 `justify-items`、`justify-self` 会被忽略，主轴方向上 flex 始终把这一行当作一个整体。

---

上一篇最后留了一句话：`inline-block` 的三个坑都来自“行内上下文是为排文字设计的，不是为排一排方块设计的”，所以 Flexbox 接手了。这一篇就来回答接手之后的关键问题：给父元素加上 `display: flex`，到底哪些规则变了；以及随之而来的主轴、交叉轴怎么判断，`justify-content` 和 `align-items` 分别在对齐哪个方向。

## 它改的是父元素的“内层类型”，以及孩子的身份

上一篇讲过，`display` 一直在同时描述两件事：外层类型决定这个盒子怎么和邻居排，内层类型决定它内部的孩子怎么排。`display: flex` 改的是**后者**。

按规范的定义，`flex` 等同于 `block flex`：外层依旧是 `block`，所以这个父元素仍然独占一行、上下接着别的块级元素，行为和普通 `div` 没有区别；变的是它内部——从常规流（normal flow）换成了 flex 布局 [1][4]。写成 `inline-flex` 就是 `inline flex`：外层变成行内级，会跟着文字首尾相接，内部同样是 flex 布局 [1][4]。日常做整行布局，用 `flex` 就够了。

这个父元素在术语里叫 **flex container（弹性容器）**，它内部参与 flex 布局的那些孩子叫 **flex item（弹性项目）**。这里有三个马上会遇到的后果：

**第一，只有直接子元素变成 flex item。** 孙子辈及更深的后代不受影响，仍然按原来的规则排 [3]。想让某一层里的东西并排，`display: flex` 必须写在它们共同的那一层父元素上。

**第二，子元素的 `display` 会被“块化”**：只要最近的一层祖先算出来是 `flex` 或 `inline-flex`，这个子元素自己算出来的 `display` 就被转换成块级版本 [3]。所以上一篇那个难题——“`span` 不能设宽高”——在 flex 容器里自动消失：作为 flex item 的 `span` 可以设 `width`、`height` 和上下 `margin`，行内身份被换成了块级身份。（只有成为 flex item 的那一层享受这个待遇。）

**第三，一批为块级常规流设计的规则在这里失效。** flex 容器建立的是一个 flex formatting context：[1][3]

- 子元素上的 `float`、`clear` 不再产生浮动和清除，也不会让元素脱离常规流；
- `vertical-align` 对 flex item 没有效果，对齐改由 flex 的属性负责；
- **相邻 flex item 的外边距不再合并**。上一篇里“上下两个块级盒子间距取较大者”的规则，在 flex 容器内部不成立；
- 容器自己的外边距也不会和内容的外边距合并。

一句话概括：`display: flex` 写在父元素上，改的却是孩子——孩子的排法变了，孩子的身份被改成块级，一批旧的块级规则被换掉了。

## 主轴由 flex-direction 决定，交叉轴就是剩下那条

flex 布局里所有对齐属性都建立在两条轴上 [2]：

- **主轴（main axis）**：由 `flex-direction` 指定的那条方向，它的默认值是 `row`；
- **交叉轴（cross axis）**：与主轴垂直的那条。

默认的 `flex-direction: row` 表示主轴沿着文字行走的方向——在中文、英文这种从左到右的文字里，就是水平向右。所以默认情况下主轴水平、交叉轴竖直。改成 `flex-direction: column`，主轴变成竖直向下，交叉轴就变成水平方向 [2]。

判断顺序只有一步：**先看 `flex-direction`，确定主轴；剩下那条就是交叉轴。** 不要按“横的是主轴、竖的是交叉轴”去记，那个印象在做侧边栏、竖向工具栏时立刻会出错。

方向上还有一个细节：`flex-start` / `flex-end` 指的是**主轴的**起点和终点，会跟着 `flex-direction` 走，换了方向它们指的位置也跟着换 [2]。

在尺寸上，flex item 有两条默认行为，先记住，下面两节都要用到：[2]

- **主轴方向默认不长大**：三个 100px 的卡片并排时，它们就保持各自 100px，不会自动撑开去填满容器——这正是下面 `justify-content` 能分到“富余空间”的前提。但空间不够时会**被压缩**，而不是溢出容器。
- **交叉轴方向默认被拉伸**：这一条来自 `align-items` 的初始值 `stretch`，后面会专门讲。

## justify- 管主轴，align- 管交叉轴

flexbox 的对齐属性只有一条记忆规则：**`justify-` 开头的管主轴，`align-` 开头的管交叉轴** [5]。它跟哪条轴是横、哪条是竖没有关系，所以换 `flex-direction` 时属性名不用改，管的方向自动跟着换。

| `flex-direction` | 主轴 | `justify-content` 对齐的方向 | 交叉轴 | `align-items` 对齐的方向 |
| --- | --- | --- | --- | --- |
| `row`（默认） | 水平 | 左右 | 竖直 | 上下 |
| `column` | 竖直 | 上下 | 水平 | 左右 |

顺带一提：`justify-items` 和 `justify-self` 在 flex 布局里会被忽略，写了没有效果——主轴方向上，flex 永远把这一行元素当作一个整体来移动 [2]。

## justify-content：分配的是主轴上多出来的那部分空间

`justify-content` 的初始值是 `normal`，行为等同于 `flex-start` [2][5]。它做的事可以说得很具体：先算出主轴上的**富余空间**——容器的可用主轴尺寸减去所有子项排完后占用的主轴尺寸——再决定把这些空间放到哪里 [2]。

- `flex-start`：空间全放到后面，子项挤在主轴起点；
- `center`：空间分成两半，子项整体居中；
- `flex-end`：空间全放到前面；
- `space-between`：把空间平均分到每两个相邻子项之间，两端不留；
- `space-around`：每个子项两侧各留一份，于是相邻间距是两端间距的两倍；
- `space-evenly`：每个间隙（包括两端）完全等宽。

这里有一个前提值得单独点出来：**主轴上一旦没有富余空间，这几个值看起来全都一样。** 这解释了一个很常见的疑问：“`flex-direction: column` 时 `justify-content` 好像没反应。”原因是容器的高度由内容撑开，内容刚好占满，自然没有多余空间可分配。想看到效果，得给容器一个明确的高度（比如 `height: 100vh` 或固定像素）。而在默认的 `row` 下，块级容器默认占满父级宽度，子项又不会自动长大，通常本来就有富余宽度，所以效果一眼可见。

## align-items：默认 stretch 才是“卡片自动等高”的来源

`align-items` 的初始值是 `stretch` [2]。这就是很多人第一次用 flex 时觉得意外的那件事：随便写三张内容长度不同的卡片，它们会自动一样高。这不是巧合，而是每个子项在交叉轴方向被拉伸到了整行的高度。

改成 `flex-start`、`center`、`flex-end` 之后，子项不再被拉伸，各自回到自己的内容高度，然后在这一行内靠上、居中或靠下。

交叉轴的“富余空间”来自哪里，和主轴不太一样：它主要来自**这一行里最高的那个元素**，或者你给容器设的交叉轴尺寸。所以 `align-items: center` 通常不需要给容器写 `height` 也能看到居中效果——矮的卡片会在“最高那张撑出来的行高”里居中。而 `justify-content` 在 `column` 下则必须先有容器高度，这个不对称值得记住。

## 一个最小可用的例子

```html
<div class="row">
  <div class="card">A</div>
  <div class="card">B</div>
  <div class="card">C</div>
</div>
```

```css
.row {
  display: flex;
  height: 120px;
  justify-content: space-between; /* 主轴（水平）：富余宽度分到卡片之间 */
  align-items: center;            /* 交叉轴（竖直）：每张卡片在行高里居中 */
  background: #f1f5f9;
}

.card {
  width: 100px;
  padding: 8px 12px;
  background: #dbeafe;
}
```

三张卡片并排、左右两端贴住容器、竖直方向各自居中。把 `align-items` 那一行删掉，卡片立刻被拉伸成 120px 高——这就是 `stretch` 默认值在做的事。把 `flex-direction` 改成 `column`，`justify-content` 会立刻改成分配竖直方向的富余空间，而 `align-items` 变成管左右对齐。

到这里，flex 布局最核心的一步就走完了：**写在父元素上的 `display: flex` 改变的是孩子的排法和身份；主轴由 `flex-direction` 决定，交叉轴是另外那条；`justify-content` 分配主轴的富余空间，`align-items` 决定子项在交叉轴上怎么站，而它的默认值 `stretch` 正是卡片自动等高的原因。** 子项多了怎么换行、谁该长大谁该缩小，是后面几篇的内容。

## 术语表

- flex container（弹性容器）：写了 `display: flex` 的那个父元素，它内部换成按 flex 规则排孩子。
- flex item（弹性项目）：flex 容器的直接子元素，只有直接子元素参与 flex 布局。
- 主轴（main axis）：由 `flex-direction` 指定的那条方向，默认 `row` 时沿文字行走方向水平延伸。
- 交叉轴（cross axis）：与主轴垂直的那条方向；主轴水平时它竖直，主轴竖直时它水平。
- 富余空间（free space）：容器在某个方向上除子项占用外剩下的空间，`justify-content` 分配的就是它。
- `align-items` 的初始值 `stretch`：默认把子项在交叉轴方向拉伸到整行高度，这是一行卡片自动等高的原因。
- 块化（blockification）：元素成为 flex item 后，它的 `display` 被转换成块级版本，于是可以设宽高和上下 margin。

## 来源

1. [MDN: display — 外层/内层类型，flex = block flex、inline-flex = inline flex](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/display)
2. [MDN: Basic concepts of flexbox — 主轴与交叉轴、justify-content 与 align-items 的初始值和取值](https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Flexible_box_layout/Basic_concepts)
3. [W3C: CSS Flexible Box Layout Module Level 1 — flex 格式化上下文、float/vertical-align 不适用、外边距不合并、display 块化](https://www.w3.org/TR/css-flexbox-1/)
4. [MDN: Relationship of flexbox to other layout methods — 哪些为块级布局设计的属性在 flex 中失效](https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Flexible_box_layout/Relationship_with_other_layout_methods)
5. [MDN: Aligning items in a flex container — justify-content 与 align-items 各管哪条轴，justify-items 被忽略](https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Flexible_box_layout/Aligning_items)

---

原文：https://pangzhengboyin.com/articles/flexbox-main-axis-and-alignment-76708db6

> **庞征博引** · 想学的，慢慢都会
>
> 庞征博引是把想学的东西写成连载的 AI 学习工具。说出想学什么，它会先了解你的基础，再把主题写成一篇篇 5–10 分钟能读完的文章；边读边问，接下来学什么跟着你走。这篇就是这样写出来的。
>
> 开始你自己的连载 → https://pangzhengboyin.com
