我见过不少人(包括我自己早期)把 flex-grow 当成”宽度占比”用:容器 1000px,三个子项分别写 grow 1 / 2 / 1,期望 250 / 500 / 250。
实际不是。grow 分配的是剩余空间,而剩余空间要先减掉每个子项的 flex-basis。
公式
剩余 = 容器宽度 − Σ flex-basis
每项增长 = 剩余 × (自身 grow / Σgrow)
最终宽度 = flex-basis + 增长
算例一:basis 不相等
容器 1000px,三个子项:
| 子项 | flex-basis | flex-grow | 分到的剩余 | 最终宽度 |
|---|---|---|---|---|
| A | 200px | 1 | 700 × 1/4 = 175 | 375px |
| B | 100px | 2 | 700 × 2/4 = 350 | 450px |
| C | 0 | 1 | 700 × 1/4 = 175 | 175px |
剩余 = 1000 − (200+100+0) = 700,Σgrow = 4。三个数加起来 1000,对得上。
所以那个”grow 按比例分宽度”的直觉,只在所有 basis 都为 0 的时候成立。而 flex: 1 恰好就是这个条件——flex: 1 展开是 flex: 1 1 0%。
算例二:flex: 1 和 flex: auto
这两个缩写的差别只在 basis:
flex: 1; /* → flex-grow:1; flex-shrink:1; flex-basis:0% */
flex: auto; /* → flex-grow:1; flex-shrink:1; flex-basis:auto */
取两个子项,内容自然宽度分别是 200px 和 600px,容器 1000px,都写 flex: 1:
flex: 1(basis 0%):剩余 = 1000 − 0 = 1000,各得 500 → 500 / 500。内容宽度完全不参与。flex: auto(basis auto):basis 就是内容宽度 200 / 600,剩余 = 1000 − 800 = 200,各得 100 → 300 / 700。内容宽的那一侧结果也宽。
同一个容器、同一个 grow,结果差 200px。这就是”我明明设了 flex: 1 为什么两边不等宽”(或者反过来”为什么不等宽”)的全部原因。
收缩:同样按权重,但权重是 basis
溢出时 flex-shrink 也是加权分配的,权重是各自的 flex-basis,不是 grow 值。
容器 300px,两个子项 basis 分别是 300px 和 100px,shrink 都是 1,总宽 400px,溢出 100px:
A 承担: 100 × 300/400 = 75 → 300 − 75 = 225px
B 承担: 100 × 100/400 = 25 → 100 − 25 = 75px
不是各缩 50px。basis 大的承担得多,因为规范是按 basis 加权的。
而收缩有下限:不能小于 min-content。这一条经常表现为”shrink 设多大都不动”,是另一个问题,单独写在 flex 子项不肯缩小那篇里。
怎么记住
写 flex: 1 之前先回答一个问题:内容宽度要不要参与分配?
- 不参与、纯粹按比例分整个容器 →
flex: 1(basis 0%) - 参与、内容宽的应该更宽 →
flex: auto(basis auto) - 固定尺寸不参与 →
flex: 0 0 48px,三个值都写出来
这一个决定能解释掉九成”宽度跟我算的不一样”。剩下那一成,去看 min-width: auto。