CSS 与布局 / 🧲 Flexbox · 简写语义与对齐边界 / 等宽列假象:flex: 1 给的是「内容盒等宽」,不是「列等宽」 待审核 4 / 14
equal columns · 等宽假象

等宽列假象:flex: 1 给的是「内容盒等宽」,不是「列等宽」

最常见的等宽列写法是给每列 flex: 1,多数时候看起来没问题——直到某一列有了和别人不同的 padding / border,它就明显比别人宽。根因接着 grow / shrink 一页的分配机制:flex-grow 是把剩余空间等量加到每列的 content box 上,而不是把每列拉到相同的 border box。于是「内容盒都一样宽」,可一旦某列额外占了 padding,它的边框盒就多出那段 padding。本页用 getBoundingClientRect 实测对照 flex: 1 与 grid 1fr 两种排布下,边框盒与内容盒各自是否等宽。

误解的来源。 flex: 1 展开为 1 1 0%:basis 0%(内容盒起点为 0)、grow 1(等比例瓜分剩余)。瓜分的落点是 content box,但 padding / border 并非事后叠加:算法先把各项的 outer 尺寸从容器内主尺寸里扣掉得到 free space,再把剩下的按 grow 分到各项的 content box 上。于是三列拿到的 content box 完全相等,而某列的 padding 让它的 border box 凭空多出 2×padding{2 \times padding}box-sizing: border-box 救不了这件事——flex base size 仍按 inner 尺寸参与算法。

1 · 拖 padding,对照边框盒与内容盒

三列内容长短不一(min-width: 0 已解除自动最小尺寸,故内容长短不影响分配)。只给中间一列padding(其余两列为 0),切换排布看实测:虚线框是各列的内容盒、外层实线框是边框盒,斜纹区即 padding。注意两种排布下「谁等宽」正好对调

图 1-1 · 只给中间一列加 padding 后两种排布的实测对照。虚线框是内容盒、实线框是边框盒、斜纹区是 padding。

2 · 两种排布,分配的「等量」对象不同

每列 flex: 1 容器 grid-auto-columns: 1fr
声明位置 每个子元素 只在父容器
等分的对象 剩余空间等量加到各列 content box 轨道(track)按 1fr 等分,列被撑满整条轨道
列有不同 padding 时 content box 等宽,border box 差 2×padding{2\times padding} border box 等宽,content box 被 padding 吃窄
受内容影响 否(basis 0% 抹平内容尺寸,且本页给项设了 min-width: 0 1fr 会:它隐含 minmax(auto, 1fr),长单词或大图会把该轨道顶宽,要真等宽须写 minmax(0, 1fr)
直觉「列等宽」 不成立(差一个 padding) 成立

不换 grid 的话,flexbox 内的补救是「把 padding 移出 flex item」。 给每列套一层无 padding 的外壳做 flex item、把 padding 放到内层包裹上:这样参与分配的外层 border box 相等,padding 只在内部消耗空间。代价是多一层标记;与 display: contents 那页「让包裹层消失」恰好相反——此处是主动加一层包裹来隔离 padding。

什么时候直接上 grid。「一组列,无论内容如何都等宽」本质是父容器统一切分轨道,这正是 grid 的模型:一句 grid-auto-columns: minmax(0, 1fr) 写在父上(裸 1fr 隐含 minmax(auto, 1fr),仍会被内容顶宽),既不需要遍历子元素加 flex: 1,也不受 padding / border / 内容影响。flex 的 content-first 模型擅长「按内容伸缩的导航、标签条」;一旦需求是「严格等宽的栅格」,grid 通常更省心。

图 2-1 · 两种等宽写法的代码对照。