CSS Flexbox:弹性布局入门 · Lesson 4 of 5

第 4 课:剩余空间的分配

本课目标:

  • 解释 flex-basisflex-growflex-shrink 各自控制什么。
  • 预测 flex-grow 如何划分自由空间(不是总宽度)。
  • 读懂 flex 简写,并知道 flex: 1 展开成什么。

先修条件:第 1-3 课(两条轴、justify-contentalign-itemsflex-direction) | 上一篇 << 03 | 下一篇 05 >>

“flex: 2 应该是两倍宽”,以及为什么通常不是

早晚有一天,你会给一个 item 写 flex: 1、给另一个写 flex: 2,然后期待第二个正好是两倍宽。有时看起来对,更多时候不对,而且你说不出为什么。原因是这些属性划分的不是 item 的宽度,而是 item 尚未占用的剩余空间。漏掉“剩余”这一个词,你设的每个比例都显得随机。这一课把“到底在分什么”钉死。

讲解

有三个属性控制 flex item 沿主轴的尺寸。一个一个来。

flex-basis 是 item 在任何自由空间分配之前的起始尺寸1。把它理解成“进入谈判之前,我想要多大”。默认值 auto 下,起始尺寸来自 item 自己的宽度或内容。

flex-grow 是一个增长因子:当有多余空间时,它决定 item 拿走多少正自由空间1。机制是比例,不是宽度。浏览器把每个 item 的增长因子加总,用总数去除自由空间,再把各自的份额分给每个 item1。所以 flex-grow: 2 挨着 flex-grow: 1,意思是第一个 item 拿到自由空间的三分之二,不是容器的三分之二。

flex-shrink 是镜像:当 item 总尺寸超出、出现自由空间需要扣除时的收缩因子1。收缩因子越高,意思是“超支时让出更多宽度”。有一个让收缩显得难以预测的细节:扣除量还会按每个 item 的基础尺寸加权,所以在收缩因子相同的情况下,宽的 item 比窄的 item 让出更多宽度1

“自由空间”这个词就是整课的核心。自由空间是把每个 item 的基础尺寸都预留之后剩下的部分。grow 和 shrink 永远只作用在这块剩余上。

这三个属性你很少分开设。flex 简写把它们合在一起:flex: 2 1 auto 表示 flex-grow: 2flex-shrink: 1flex-basis: auto,而且指南推荐用简写代替单独的属性2。常见的一行 flex: 1 展开为 flex: 1 1 0:grow 1、shrink 1、basis 0。因为 basis 是 0,每个 item 都从零起步,整条行都成了自由空间,所以给几个 item 都设 flex: 1 会让它们无视内容、真正等宽1

下面的导航栏有 logo、搜索框和按钮。现在搜索框只有占位文字那么宽。让它长开、填满中间的空白,同时 logo 和按钮保持自然尺寸。

只有搜索框拿到了增长因子,所以只有它认领自由空间。logo 和按钮的 grow 是 0,所以它们保持内容需要的大小不变。这就是日常的“一个弹性元素、几个固定元素”导航模式。

完整示例(跟着做)

一个三栏布局,中间栏的弹性是两侧的两倍:

因为三个都用 flex-basis: 0,整个宽度都是自由空间,按增长因子 1 : 2 : 1 划分。因子总数是 4,所以每个 .side 拿到宽度的四分之一,.main 拿到一半。这里 flex: 2 确实意味着“两倍于一侧”,但只因为 basis 是 0,它把整条行都变成了自由空间。把 basis 改成 auto,比例就会作用在剩余空间上了。

你来试试(填空示例)

你想要一个固定 240px 的侧边栏,和一个吃掉其余全部空间的内容区。填上两个 flex 简写:

答案:.sidebar { flex: 0 0 240px; }(grow 0、shrink 0、basis 240px,它原地不动)和 .content { flex: 1; }(grow 1,吸收全部自由空间)。把每个简写读成“grow / shrink / basis”,意图一目了然:侧边栏拒绝伸缩,内容区拿走剩下的。

小结 + 下节课预告

flex-basis 设定 item 的起始尺寸;flex-growflex-shrink 按各自因子的比例划分正或负的自由空间flex 简写把三者打包,flex: 1 等于 1 1 0。到目前为止一切都假设 item 只有一行。最后一课我们用 flex-wrap 让 item 换到多行、用 gap 拉开间距,然后你从零搭一个真实的响应式导航栏和卡片行。

Footnotes

  1. MDN: Controlling ratios of flex items along the main axis — https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Flexible_box_layout/Controlling_flex_item_ratios 2 3 4 5 6

  2. CSS-Tricks: A Complete Guide to Flexbox — https://css-tricks.com/snippets/css/a-guide-to-flexbox/

Exercises

01

在你自己的文件里,搭一个 display: flex 的行,两个子元素。给其中一个 flex-grow: 1,另一个保持默认。刷新,看哪一个吸收了空白。然后给两个都设 flex-grow: 1。

第 1 级(热身)
Done criteria · checked locally
02

在一条宽于 400px 的行里,把两个 item 分别设为 flex: 1 1 200px 和 flex: 3 1 200px。刷新前先预测各自的最终宽度,然后在 devtools 里量。

第 2 级(进阶)
Done criteria · checked locally