本文要点:
- flex 布局中,flex 设置单、双、三值的时候分别有什么意义。
- flex 布局怎么压缩负空间(flex-shrink)
- flex 布局怎么分配多余的空间(flex-grow)
- flex-basis 和 flex-grow 怎么配合分配空间
例子
先定义结构:
<div class="list">
<div class="item singleValue">先帝创业未半</div>
<div class="item singleValue">而中道崩殂</div>
<div class="item singleValue">今天下三分</div>
</div>
<div class="list">
<div class="item doubleValue">先帝创业未半</div>
<div class="item doubleValue">而中道崩殂</div>
<div class="item doubleValue">今天下三分</div>
</div>
基础样式:
.list {
display: flex;
box-sizing: border-box;
width: 300px;
margin: 10px;
}
.item {
background: pink;
}
.item:nth-child(2) {
background: lightblue;
}
flex 单值
无单位的时候,flex 设置了单值,相当于 flex-grow ,表示子项的弹性比例:
.singleValue {
/* 单值无单位相当于 flex-grow ,定义弹性盒子项(flex item)的拉伸因子 */
flex: 1;
}
.singleValue:first-child {
/* 等同于 flex: 2 */
flex-grow: 2;
}
效果:

.singleValue:last-child {
/* 单值带单位,相当于 flex-basis */
/* flex: 75px; */
flex-basis: 75px;
}
效果:

我发现第三个子项并不是期待的 75px ,这个疑问稍后再讲。
双值或者三值
双值第一个为 flex-grow , 第二个如果有单位,是 flex-basis
.doubleValue {
flex: 1 300px;
}
List 总宽度 300 ,每个子项 300px ,远远超出了 flex 弹性盒子的范围,因此会根据 flex 的算法自动调整子项宽度如下:

此时设置三值:
.doubleValue:first-child {
flex: 1 0 300px;
}
等同于:
.doubleValue:first-child {
flex-shrink: 0;
}
也就是说,双值 第一个 flex-grow ,第二个无单位的话,是 flex-shrink 。
三值的话,分别是 flex-grow | flex-shrink | flex-basis.
效果如下:

flex-shrink 的计算方式
当我们给子项设置的宽度之和大于父元素的宽度时,flex-shrink 就派上用场了。
默认情况下,每个子项的 flex-shrink 的值是 1. 所有子项会等比例自动压缩,省心省力。
实际项目中比较常用的 flex-shrink 的值,可能会是 0 或者 1,默认是 1 ,不想让子项被自动压缩空间的话,设为 0 就好了。
而在 flex-shrink 的官方定义里,我发现 flex-shrink 的值可以是 0 到无穷大,即非负值。那么 flex-shrink 怎么计算压缩的值呢?
我把上边双值 class 改为:
.doubleValue {
flex-basis: 150px;
}
.doubleValue:first-child {
flex-basis: 300px;
flex-shrink: 2;
}
得到了三个 100px 宽的子项:

在 drafts.csswg.org/css-flexbox… 里对 flex-shrink 压缩因子的定义是:
…which determines how much the flex item will shrink relative to the rest of the flex items
似懂非懂。实际上我们使用 flex 布局时并不需要了解 flex-shrink 是怎么分配空间的,浏览器为我们准备好了一切。
我在 Day 24: flex-shrink calculation | SamanthaMing.com 里找到一个算法:

验证下:
计算 shrunk space:
Shrunk space = (300 +150 + 150) - 300 = 300
有 300px 的多余空间需要压缩。
计算 shrink ratio
total shrink scaled width = 300 * 2 + 150 * 1 + 150 * 1 = 900
first child Shrink ratio = 300 * 2 / 900 = 0.666…
Nth-child(2/3) shrink ratio = 150 * 1 / 900 = 0.166…
计算新的宽度
first child new width = 300[flex-basis value] - (300 [shrunk space]* 0.666..)= 100px
Nth-child(2/3) new width = 150 - 300 * 0.166… = 100px
因此才会有被等分的三个子项:

例外情况
我想让子项好看点儿,于是加了 padding :
.item { padding: 10px; background: pink; }
看看我得到了什么:

改为:
.item {
box-sizing: border-box;
padding: 10px;
background: pink;
}

上述公式有效。
flex-grow
子项宽度之和小于父元素宽度时,flex-grow 的设置就很有必要了。
依然借用 www.samanthaming.com/flexbox30/2… 里的一个图来说明 flex-grow 如何分配剩余空间:

.doubleValue {
flex-grow: 1;
flex-basis: 50px;
}
.doubleValue:first-child {
flex-grow: 2;
flex-basis: 100px;
}
我得到了 150px、75px、75px:

需要被分配的剩余空间 = 300 - (100 + 50 + 50) = 100px
Flex-grow 总份数 = 2 + 1 + 1 = 4
First-child 可以被分配的多余空间 = 100 * 2/4
其它子项可以被分配的多余空间 = 50 * 1/4
first-child 的最终宽度: 100 + 100 * 2/4 = 150px
其它子项最终宽度:50 + 100*1/4 = 75px
回头再看看之前的 flex-basis 设置
.singleValue {
flex: 1;
}
.singleValue:first-child {
flex-grow: 2;
}
.singleValue:last-child {
flex-basis: 75px;
}
分别得到:112.5px、56.25px、131.25px:

前两个子项按比例分配,第三个在按比例的基础上增加了 flex-basis 的设置,于是得到了 flex-grow 为 1 时自动分配的空间 + flex-basis 的总和。
接着改为:
.singleValue {
flex: 1;
flex-basis: 10px;
}
.singleValue:first-child {
flex-grow: 2;
}
.singleValue:last-child {
flex-basis: 20px;
}

现在,三个子项的宽度分别是:140px、75px、85px 。
回头看 flex-basis 的定义:
flex-basis 指定了 flex 元素在主轴方向上的初始大小。如果不使用 box-sizing 改变盒模型的话,那么这个属性就决定了 flex 元素的内容盒(content-box)的尺寸。
flex-basis 的默认值是 auto .