等宽列假象: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——flex 在算分配时不把 padding / border 计入,它们是分配完后再「叠加」到外侧的。所以三列拿到的 content box 完全相等,而某列的 padding 让它的 border box 凭空多出
。
1 · 拖 padding,对照边框盒与内容盒
三列内容长短不一(min-width: 0 已解除自动最小尺寸,故内容长短不影响分配)。只给中间一列加 padding(其余两列为 0),切换排布看实测:虚线框是各列的内容盒、外层实线框是边框盒,斜纹区即
padding。注意两种排布下「谁等宽」正好对调。
2 · 两种排布,分配的「等量」对象不同
每列 flex: 1 |
容器 grid-auto-columns: 1fr |
|
|---|---|---|
| 声明位置 | 每个子元素上 | 只在父容器上 |
| 等分的对象 | 剩余空间等量加到各列 content box | 轨道(track)按 1fr 等分,列被撑满整条轨道 |
| 列有不同 padding 时 | content box 等宽,border box 差 ${2\times padding}$ | border box 等宽,content box 被 padding 吃窄 |
| 受内容影响 | 否(basis 0% 已抹平内容尺寸) |
否(1fr 只看轨道,不看内容) |
| 直觉「列等宽」 | 不成立(差一个 padding) | 成立 |
不换 grid 的话,flexbox 内的补救是「把 padding 移出 flex item」。 给每列套一层无 padding 的外壳做 flex item、把 padding 放到内层包裹上:这样参与分配的外层 border box 相等,padding 只在内部消耗空间。代价是多一层标记;与
display: contents 那页「让包裹层消失」恰好相反——这里是主动加一层包裹来隔离 padding。
什么时候直接上 grid。「一组列,无论内容如何都等宽」本质是父容器统一切分轨道,这正是 grid 的模型:一句 grid-auto-columns: 1fr 写在父上,既不需要遍历子元素加 flex: 1,也不受 padding / border / 内容影响。flex 的 content-first
模型擅长「按内容伸缩的导航、标签条」;一旦需求是「严格等宽的栅格」,grid 通常更省心。