# 主轴上多出来的空间怎么分：flex-basis、flex-grow、flex-shrink 与 flex 简写的默认值

flex-basis 决定起算点，flex-grow 按比例分正富余空间，flex-shrink 按“因子×基准尺寸”分摊缺额，而 flex 简写省略的 basis 是 0% 不是 auto

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

## 本篇要点

1. flex item 的最终主轴尺寸按三步得到：先由 flex-basis 定基准尺寸，再用基准之和与容器主轴尺寸的差值判断是正富余还是负富余，最后分别交给 flex-grow 或 flex-shrink 分配。
2. flex-basis 的初始值是 auto，意思是回头看主轴方向的 width（column 时是 height），width 也是 auto 时才退回内容宽度；写具体长度或百分比时会压过 width。
3. flex-grow 分配的是富余空间的比例，不是最终宽度的比例：600px 容器里三个 100px、grow 为 1/2/3 的项得到 150/200/250px，因为每项都在自己的原始宽度上加了一份空间。
4. 三项都写 flex-grow: 1 不会得到等宽结果，除非同时把 flex-basis 设为 0，让内容宽度不参与计算。
5. flex-shrink 分摊缺额时用“shrink 因子 × 基准尺寸”作为权重，所以收缩因子相同时每项按同一个比例缩小，宽度比例得以保留；flex-shrink: 0 的项完全不缩。
6. flex item 默认不会缩到小于内容的最小尺寸，长且不可断行的文本会拒绝收缩，需要 min-width: 0 才能继续缩下去。
7. flex 简写中省略的项用的是简写自己的默认值：grow 按 1、shrink 按 1、basis 按 0%，而不是长写属性的初始值 0、1、auto，所以 flex: 1 等于 1 1 0%，flex: 2 2 等于 2 2 0%。
8. flex: 1 和 flex: auto 效果不同，因为前者 basis 为 0%、后者为 auto；只写 flex-grow: 1 等于 flex: auto，不是 flex: 1。

---

上一篇结尾留了一条线索：flex 项目在主轴方向默认不长大，空间不够时又会被压缩。这两个默认行为来自三个属性——`flex-grow`、`flex-shrink`、`flex-basis`，而它们平时几乎都是以 `flex` 简写的形式出现的。这一篇就按它们实际参与计算的顺序，把“一项最终有多宽”这件事讲清楚。

## 第一步：先定“基准尺寸”，这由 flex-basis 负责

伸缩发生之前，每一项在主轴方向上要先有一个起始大小，这个起始大小叫 **flex basis（基准尺寸）**，由 `flex-basis` 指定。

它的初始值是 `auto`，含义是“去问主轴方向的尺寸属性要答案”：`flex-direction: row` 时看 `width`，`column` 时看 `height`。也就是说：

- 写了 `width: 200px`，基准尺寸就是 200px；
- `width` 也是 `auto`，就看内容本身——更准确地说，是内容在不需要换行时要占的宽度（`max-content`）。

`flex-basis` 一旦写成具体长度（如 `flex-basis: 100px`）或百分比，它就**压过 `width`**。同时写了 `width: 200px` 和 `flex-basis: 100px`，起算点是 100px，不是 200px。百分比相对于容器内容盒的主轴尺寸 [3]。

可以把两者的分工记成一句：`width` 是常规流里的尺寸，`flex-basis` 是“伸缩开始之前你在主轴上的起点”。对 flex item 来说，起点由 `flex-basis` 决定 [2][3]。

## 第二步：把基准尺寸加起来，算出富余空间

所有项的基准尺寸相加，再和 Flex 容器主轴方向可用的空间比一比，差出来的那部分就叫 **富余空间（free space）** [1]：

- 结果为正：容器比内容宽，叫正富余空间，交给 `flex-grow` 处理；
- 结果为负：内容比容器宽，叫负富余空间，交给 `flex-shrink` 处理。

这一步能解释一个常见困惑：`flex-grow` 在空间刚好或者不够时完全没有表现，`flex-shrink` 在空间够用时也完全没有表现。它们各自只负责一半情况。这跟上一篇 `justify-content` 在没有富余空间时几个值看起来一样，是同一类原因。

```mermaid
flowchart LR
  A["每项按 flex-basis 取基准尺寸"] --> B["基准尺寸之和 与 容器主轴尺寸比较"]
  B --> C{"差值是正还是负？"}
  C -->|"正：有富余"| D["flex-grow 按比例分配富余"]
  C -->|"负：不够"| E["flex-shrink 按比例扣除缺额"]
  D --> F["得到最终主轴尺寸"]
  E --> F
```

## flex-grow：分的是“多出来的那部分”，而且只是比例

`flex-grow` 的初始值是 0，意思是“有富余也别给我”。设成正数，它才参与分配。

关键是：`flex-grow` 的数值表示的**不是最终宽度的比例，而是富余空间的分法**。看一个具体例子：容器主轴 600px，三个盒子 `width` 都是 100px，`flex-grow` 分别是 1、2、3。

1. 基准尺寸之和：$100+100+100=300$px；
2. 富余空间：$600-300=300$px；
3. grow 因子之和：$1+2+3=6$，所以每份是 $300\div6=50$px；
4. 各自拿到：$1\times50$、$2\times50$、$3\times50$，即 50px、100px、150px。

最终宽度是 150px、200px、250px，比值是 3:4:5，不是 1:2:3。grow 只影响“多出来的 300px 怎么分”，原本的 100px 谁也不动 [4]。

这正好解释了很多人第一次用它的困惑：三个宽度不同的盒子都写 `flex-grow: 1`，富余空间是平分的，但每一项是在自己的原始宽度上加上同样一份，所以结果仍然不等宽。如果希望它们最终完全一样宽，就不能让“原始宽度”参与计算——那是下面要讲的 `flex-basis: 0`。

## flex-shrink：不够的空间按“基准尺寸的比例”扣

`flex-shrink` 的初始值是 1，这就是“主轴方向空间不够时会被压缩”的来源。

它的算法和 grow 有个重要差别：每个项的收缩因子会**先乘上自己的基准尺寸**，得到一个缩放后的因子，再按这个因子去分摊负富余空间 [2][4]。原因是让大块头多出一点血，小块头不至于先被压没。

容器 600px，三个盒子 `flex-basis` 分别是 400px、200px、300px，`flex-shrink` 都是 1：

1. 基准尺寸之和 900px，负富余空间 300px；
2. 缩放后的因子：$1\times400$、$1\times200$、$1\times300$，总和 900；
3. 各自被扣掉 $300\times\frac{400}{900}\approx133.3$、$300\times\frac{200}{900}\approx66.7$、$300\times\frac{300}{900}=100$。

结果是 266.7px、133.3px、200px，加起来正好 600px。顺带能看出：收缩因子相同时，每一项都按同一个比例缩小（这里都变成原来的 $\frac{2}{3}$），所以它们之间的宽度比例被保留了下来。

如果某一项写 `flex-shrink: 0`，它完全不缩，缺额只能由其他项分担；如果所有项都不缩，容器就溢出。

还有个更隐蔽的下限：flex item 默认**不会缩到比自己内容的最小尺寸（`min-content`）还小**。一段长长的、中间不能断行的文字，会把它的最小尺寸撑得很大，于是它拒绝继续缩，容器被撑破或者出现横向滚动条。想让文字换行、继续缩下去，需要给这一项加上 `min-width: 0`（`column` 方向则是 `min-height: 0`）[1][5]。这是实际写页面时最常见的 flex 触发点之一。

## flex 简写：省略的项用的是简写自己的默认值

`flex` 是 `flex-grow`、`flex-shrink`、`flex-basis` 的简写。它的坑不在语法，而在**省略时的默认值**：简写里省略的 grow 和 shrink 分别按 1 和 1 处理，省略的 basis 按 `0%` 处理——这不是三个长写属性的初始值（0、1、`auto`）[2][5]。

| 写法 | 展开为 | 效果 |
| --- | --- | --- |
| `flex: 1` | `1 1 0%` | 基准按 0 算，整个容器空间按 grow 比例分，三栏等宽 |
| `flex: 2 2` | `2 2 0%` | 第二个值是 shrink，basis 被一起改成 `0%` |
| `flex: 1 30px` | `1 1 30px` | 第二个值是长度，就当 basis 用 |
| `flex: auto` | `1 1 auto` | 基准来自 width 或内容，富余空间仍会吸收 |
| `flex: initial` | `0 1 auto` | 不占富余空间，但不够时会缩（默认值） |
| `flex: none` | `0 0 auto` | 完全不伸缩，宽度按 width 或内容 |

由此能看清两组最容易被混淆的写法。

**`flex: 1` 和 `flex: auto` 不是一回事。** 把三个宽度分别是 100px、200px、50px 的盒子放进 600px 的容器：

- 都写 `flex: 1`（即 `1 1 0%`）：基准全部归零，600px 全是富余空间，平分后每项 200px，三栏等宽——因为我们主动丢掉了各自的内容宽度。
- 都写 `flex: auto`（即 `1 1 auto`）：基准是 100、200、50，共 350px，富余 250px 平分，每项分到 83.3px，结果是 183.3px、283.3px、133.3px，仍然不等宽。

所以“给每项 `flex: 1` 就等宽”成立的前提是 basis 归零，而不是 grow 等于 1 本身。

**只写 `flex-grow: 1` 和写 `flex: 1` 也不一样。** `flex-grow: 1` 只动了 grow，`flex-basis` 还是初始值 `auto`，等于 `1 1 auto`——也就是 `flex: auto` 的效果，起步宽度还带着内容。很多人写下 `flex-grow: 1` 期待等宽三栏却没得到，原因就在这里。

这也是 MDN 建议优先用简写关键字（`auto`、`initial`、`none`）或单位数字的原因：单独改一个长写属性，很容易留下一个自己没想清楚的组合 [3][5]。

## 怎么算出一项最终多宽

按顺序问四个问题就够了：

1. **基准多大？** 看 `flex-basis`；是 `auto` 就退回 `width`，再退回内容宽度。
2. **空间够不够？** 基准尺寸之和与容器主轴尺寸相减，得正走 grow，得负走 shrink。
3. **分多少？** grow：富余空间按 grow 因子之比切分，加回基准；shrink：缺额按“shrink 因子 × 基准尺寸”之比扣除。
4. **有没有到底？** 缩不下去时检查 `min-width: auto` 这道下限，必要时改成 `0`。

把这三条长写属性记成一句话：`flex-basis` 决定从哪儿出发，`flex-grow` 决定多出来的怎么分，`flex-shrink` 决定不够的怎么扣；而 `flex` 简写最大的坑，是省略的 basis 会变成 `0%` 而不是 `auto`。

## 术语表

- flex-basis（基准尺寸）：伸缩开始之前，flex item 在主轴方向上的起始大小；默认 auto 表示回头看 width 或内容宽度。
- 富余空间（free space）：所有项的基准尺寸之和与容器主轴可用尺寸的差值，为正时归 flex-grow 分配，为负时归 flex-shrink 分摊。
- flex-grow（伸展因子）：只在有正富余空间时生效，数值代表在富余空间里占的比例，不是最终宽度的比例。
- flex-shrink（收缩因子）：只在有负富余空间时生效，分摊时先乘上该项的基准尺寸，所以大项会扣除更多。
- flex 简写的省略默认值：简写里省掉的 grow、shrink、basis 分别按 1、1、0% 处理，与三个长写属性的初始值不同。
- 自动最小尺寸（min-width：auto）：flex item 默认不会缩到小于内容最小尺寸，必要时用 min-width: 0 解除这一下限。

## 来源

1. [MDN: Controlling ratios of flex items along the main axis — flex-basis、flex-grow、flex-shrink 与正负富余空间](https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Flexible_box_layout/Controlling_flex_item_ratios)
2. [CSS Flexible Box Layout Module Level 1 — flex 简写的展开规则](https://www.w3.org/TR/css-flexbox-1/)
3. [MDN: flex-basis — 取值、auto 的解析方式与优先于 width](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/flex-basis)
4. [MDN: flex-grow — 分配算法与含具体数字的示例](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/flex-grow)
5. [MDN: flex — 各种写法、关键字展开值与 min-content 下限](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/flex)

---

原文：https://pangzhengboyin.com/articles/flex-grow-shrink-basis-and-flex-shorthand-b52c0723

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