等宽列假象: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 凭空多出
。box-sizing: border-box 救不了这件事——flex base size 仍按 inner 尺寸参与算法。
1 · 拖 padding,对照边框盒与内容盒
三列内容长短不一(min-width: 0 已解除自动最小尺寸,故内容长短不影响分配)。只给中间一列加 padding(其余两列为 0),切换排布看实测:虚线框是各列的内容盒、外层实线框是边框盒,斜纹区即
padding。注意两种排布下「谁等宽」正好对调。
2 · 两种排布,分配的「等量」对象不同
每列 flex: 1 |
容器 grid-auto-columns: 1fr |
|
|---|---|---|
| 声明位置 | 每个子元素上 | 只在父容器上 |
| 等分的对象 | 剩余空间等量加到各列 content box | 轨道(track)按 1fr 等分,列被撑满整条轨道 |
| 列有不同 padding 时 | content box 等宽,border box 差 | 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 通常更省心。