图解CSS:Flexbox布局(Part2)
运用于Flex项目属性
运用于Flex项目中的CSS属性和运用于Flex容器上的属性类似,主要分为三个部分:对齐、尺寸 和 方向 三个部分:

align-self
在Flex容器中可以使用align-items来控制来控制Flex项目(所有Flex容器的子元素)在Flex容器侧轴方向的对齐方式。在Flexbox布局模块中,还可以使用align-self对单个Flex项目进行对齐方式的控制,同样是Flex容器侧轴方向的对齐。
align-self属性可接受的值主要有:
align-self: auto | flex-start | flex-end | center | baseline | stretch;
即使在Flex容器上显式设置了align-items用来控制所有Flex项目在Flex容器侧轴上的对齐方式,但只要在Flex项目上显式设置了align-self的值,则会覆盖align-items属性。只不过只对显式设置了align-self的Flex项目生效。比如下面这个示例,我们在Flex容器显式设置了align-items的值为center,并且对单数Flex项目显式设置align-self的值:
.flex__container {
align-items: center;
direction: ltr;
writing-mode: horizontal-tb;
flex-flow: row nowrap;
}
.flex__item:nth-child(odd) {
algin-self: var(--flexSelf);
}
你将看到的效果如下:

这里需要特别提出的一点是:Flex项目的align-self显式设置值为auto时不会覆盖Flex容器的align-items;另外如果在Flex项目上显式设置margin的值为auto时,Flex项目的align-self值将会失效。
同样的,algin-self也会受direction和writing-mode取值的影响。
当Flex容器中有多行时,每一行会进行独立的布局,其中align-self属性每次仅作用于单独一行上的Flex项目;同时每一行侧轴长度指定为可容纳该行中的Flex项目(在依据align-self属性进行对齐之后)的最小值,并且这些行依据align-content属性在Flex容器中进行对齐。
order
在Flexbox布局模块中,我们除了在Flex容器上通过flex-direction来改变DOM源的顺序之外,还可以在Flex项目上显式设置order属性来改变DOM源顺序:
order: 0 || <number>
其中0为order的默认值,同时它可以接受正负值(整数值)。
比如上面示例中,每组中的第二个Flex项目order设置了不同的值:
- 第一组中Flex项目未显式设置
order值(即默认值为0) - 第二组中第二个Flex项目显式设置
order的值为1,这个时候该Flex项目会排列在最末尾 - 第三组中第二个Flex项目显式设置
order的值为-1,这个时候该Flex项目会排列在最前面

其中order值越大,Flex项目越后;反之越前:
order用来控制Flex项目顺序在布局中非常的实用,比如我们要构建一个多列布局:
<!-- HTML -->
<header>Header Section</header>
<main>
<article>Article Section</article>
<nav>Nav Section</nav>
<aside>Aside Section</aside>
</main>
<footer>Footer Section</footer>
其中<article>放在<nav>和<aside>前面,主要是为了内容为先。针对这样的DOM结构,如果我们希望<nav>在<article>左侧,<aside>在<article>右侧时,order属性就可以起关键性的作用:
上面的示例中,在main:hover改变了<nav>和<aside>两个元素的order值:
nav {
order: -1;
}
main:hover nav {
order: 1;
}
main:hover aside {
order: -1;
}
这个时候看到的效果如下:

flex
flex属性主要用来让Flex项目如何伸长或缩短以适应Flex容器中的可用空间。它是一个简写属性,即:
flex: [<flex-grow><flex-shrink>?||<flex-basis>]
其中:
flex-grow: <number>
flex-shrink: <number>
flex-basis: content | <width>
这几个属性都有其初始值:
flex-grow的初始值为0flex-shrink的初始值为1flex-basis的初始值为auto
即 flex的初始值为0 1 auto。换句话说,如果需要重置flex属性的话,可以将其值设置为none。它的实际语法是:
flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
在Flexbox布局模块中,flex是一个复杂的属性,从上面的语法规则上不难发现,flex属性可以指定 1个值(单值语法)、2个值(双值语法) 或 3个值(三值语法)。
单值语法:值必须为以下其中之一:
- 一个无单位的数(
<number>),比如flex: 1,这个时候它会被当作<flex-grow>的值 - 一个有效的宽度(
width)值,比如flex: 30vw,这个时候它会被当作<flex-basis>的值 - 关键词
none、auto或initial(即初始值)
有关于
initial更详细的介绍,可以阅读《图解CSS:CSS层叠和继承》一文。
双值语法:第一个值必须为一个无单位数值,并且它会被当作<flex-grow>的值;第二个值必须为以下之一:
- 一个无单位的数(
<number>),它会被当作<flex-shrink>的值 - 一个有效的宽度(
width)值,它会被当作<flex-basis>的值
比如:
flex: 1 2 » <flex-grow> <flex-shrink> » flex-grow: 1; flex-shrink: 2
flex: 2 30vw » <flex-grow> <flex-basis> » flex-grow: 2; flex-basis: 30vw

三值语法:
- 第一个值必须是一个无单位数(
<number>),并且它会被当作<flex-grow>的值 - 第二个值必须是一个无单位数(
<number>),并且它会被当作<flex-shrink>的值 - 第三个值必须为一个有效的宽度(
width)值,并且它会被当作<flex-basis>的值
比如:
flex: 1 2 30vw » <flex-grow> <flex-shrink> <flex-basis>
//等效于
flex-grow: 1;
flex-shrink: 2;
flex-basis: 30vw;
综合上述,flex属性的取值可以是:
auto:Flex项目会根据自身的width和height来确定尺寸,但Flex项目根据Flex容器剩余空间进行伸缩。其相当于flex: 1 1 autoinitial:Flex项目会根据自身的width和height来设置尺寸。它会缩短自身以适应Flex容器,但不会伸长并吸收Flex容器中的额外剩余空间来适应Flex容器。其相当于flex: 0 1 autonone:Flex项目会根据自身的width和height来设置尺寸。它是完全非弹性的(既不会缩短,也不会伸长来适应Flex容器)。其相当于flex: 0 0 auto<flex-grow>:定义Flex项目的flex-grow属性,取值为<number><flex-shrink>:定义Flex项目的flex-shrink属性,取值为<number><flex-basis>:定义Flex项目的flex-basis属性。若值为0,则必须加上单位,以免被视作伸缩性
在大多数情况下,开发者需要将flex设置为auto、initial、none或一个无单位的正整数。比如下面这个示例,你可以尝试着调整Flex容器的大小,来观察flex取这些值的差异:
在上面示例的基础上,我们可以给每个Flex项目的flex设置三个值:
事实上,flex在Flexbox布局模块中是一个复杂的属性,为了更好的向大家阐述清楚该属性,接下来主要分三个部分来阐述。即flex的三个子属性:flex-grow(扩展比率)、flex-shrink(收缩比率)和 flex-basis(伸缩基准)。这三个属性可以控制Flex项目,具体的表现如下:
flex-grow:设置Flex项目的扩展比率,让Flex项目得到(扩展)多少Flex容器剩余空间(Positive Free Space),即Flex项目可能会变大flex-shrink:设置Flex项目收缩比率,让Flex项目减去Flex容器不足的空间(Negative Free Space),即Flex项目可能会变小flex-basis:Flex项目未扩展或收缩之前,它的大小,即指定了Flex项目在主轴方向的初始大小
也就是说,flex-grow、flex-shrink和flex-basis可以用来指定或改变Flex项目的大小。在具体介绍这几个属性之前,先给大家介绍几个概念。
剩余空间和不足空间
在Flexbox布局模块中,Flex容器中可能会包含一个或多个Flex项目。而Flex容器和Flex项目都有其自身的尺寸大小,那么就会有Flex项目尺寸大小之和大于或小于Flex容器的情景:
- 当所有Flex项目尺寸大小之和小于Flex容器时,Flex容器就会有多余的空间没有被填充,那么这个空间就被称为 Flex容器的剩余空间(Positive Free Space)
- 当所有Flex项目尺寸大小之和大于Flex容器时,Flex容器就没有足够的空间容纳所有Flex项目(Flex项目会溢出Flex容器),那么多出来的这个空间就被称为不足空间(Negative Free Space),也被称为负空间
举个例子向大家阐述这两种情形:“假设我们有一个Flex容器,显式给其设置了width为80vw,并且该Flex容器中包含了四个Flex项目,每个Flex项目的width为10vw”。这个时候所有Flex项目的宽度总和则是10vw x 4 = 40vw,那么Flex容器将会有剩余空间,即80vw - 40vw = 40vw。这个 40vw 就是Flex容器的剩余空间:

再来看第二种情形:“假设Flex容器width为40vw,并且每个Flex项目的width为20vw”。这个时候所有Flex项目的宽度总和则是20vw x 4 = 80vw,那么Flex容器则没有足够的空间容纳Flex项目(Flex项目会溢出Flex容器),即40vw - 80vw = -40vw。这个 **40vw**就是我们所说的Flex容器的不足空间:

上面演示的是主轴在x轴方向,如果主轴变成y轴的方向,同样存在上述两种情形,只不过width变成了height。
CSS尺寸属性和值
如果你有阅读过《图解CSS: 元素尺寸的设置》、《图解CSS:CSS逻辑属性》以及《CSS 盒模型》的话,你会发现在CSS中可以通过不同的属性以及不同的值来设置元素大小。比如,在CSS中我们可以通过下面这些属性给Flex项目显式指定尺寸:

能用于上图中属性的值,可以是下面这些:
auto:设置值为auto时,容器的大小将会以容器的内容来计算,它将是一个自动大小;不过给min-width、min-height、min-inline-size或min-block-size设置值为auto时,将会指定一个自动计算好的最小值none:如果取值为none时,元素盒子的大小是没有任何限制<length-percentage>:使用<length>或<percent>指定元素的大小,在适当情况下,根据其父元素的宽度来解析百分比min-content:如果指定了内联轴,那么min-content对应的大小则是内联大小,否则将表现为属性的初始值,即固有的最小宽度max-content:如果指定了内联轴,那么max-content对应的大小则是内联大小,否则将表现为属性的初始值,即固有的首选宽度fit-content():如果显式指定了内联轴,使用fit-content()函数,可以用指定的参数替换可用空间,即min(max-content, max(min-content, <length-percentage>));否则将表现为属性的初始值。对于内在尺寸,fit-content(<length>)表现长度值(<length>)。如果fit-content()使用了百分比值,将会作为min-content当作最小内容,max-content作为最大内容
而其中auto、min-content、max-content和fit-content()又被称为自动计算尺寸大小方式。
在CSS中除了width、height、inline-size和block-size会影响元素尺寸大小之外,还有运用于元素盒子的属性,比如内距,边框等都会影响元素尺寸大小。有关于这方面更详细的介绍可以阅读《CSS 盒模型》一文。
内部尺寸和外部尺寸
熟悉CSS盒模型的同学都知道,根据盒子不同的边缘可以将盒子划分成不同类型的盒子,比如content-box、padding-box、border-box和margin-box:

其中content-box对应的尺寸被称为内部尺寸,而margin-box对应的尺寸被称为外部尺寸。
明确的尺寸和不确定的尺寸
明确的尺寸指的是不需要执行布局就可以确定盒子的大小。也就是说,显式给容器设置一个固定值、或内容所占区域的大小、或一个容器块的初始大小,或通过其他计算方式得到的尺寸(比如Flexbox布局中的“拉伸和压缩(Strretch-fit)”)。
不确定的尺寸指的是一个未知的尺寸。换句话容器的大小具备无限空间(即有可能很大,也有可能很小)。
通俗一点来理解的话,明确的尺寸是知道容器的width(或inline-size)和height(或block-size);不确定的尺寸是需要根据内容来计算的,所以要知道不确定的尺寸需要先检查内容。
有了上述概念之后,我们就可以开始进入flex-grow、flex-shrink和flex-basis三个属性的世界。
flex-grow
flex-grow的语法规则很简单:
flex-grow: <number>
其中<number>是一个正数的数值,并且其默认值是0。
在Flexbox布局模块中,flex-grow是一个扩展因子,也被称为扩展比例。其意思是,当Flex容器有一定的剩余空间时,flex-grow可以让Flex项目分配Flex容器剩余的空间,每个Flex项目将根据flex-grow因子扩展,从而让Flex项目占满整个Flex容器(有效的分配和利用Flex容器剩余空间)。
当flex-grow的值是一个大于0的值时,Flex项目就会占用Flex容器剩余空间。在Flexbox布局模块中,在使用flex-grow时可以按下面的方式使用:
- 所有Flex项目设置相同的
flex-grow值 - 每个Flex项目设置不同的
flex-grow值
正如上例所示,在Flex项目中设置flex-grow之后,Flex项目会根据Flex容器宽度来动态调整自身的尺寸大小:

当Flex容器没有任何剩余空间时,即使Flex项目显式设置了flex-grow的值,Flex项目也不会扩展(因为没有空间可扩展):

正如上图所示,flex-grow的不同设置得到的效果将会不一样,但flex-grow的值始终总量为1,也就是说,所有Flex项目的占有量总和(分子)和分母相同。
如果你希望能更彻底的了解flex-grow,那就需要了解flex-grow的计算方法。flex-grow有相应的计算公式:

下面用一个简单的示例来阐述flex-grow是如何分配Flex容器的剩余空间:
<!-- HTML -->
<div class="flex__container">
<div class="flex__item" style="--flexGrow: 0">flex-grow:0</div>
<div class="flex__item" style="--flexGrow: 1">flex-grow:1</div>
<div class="flex__item" style="--flexGrow: 2">flex-grow:2</div>
<div class="flex__item" style="--flexGrow: 3">flex-grow:3</div>
</div>
/* CSS */
:root {
--flexGrow: 0;
}
.flex__container {
inline-size: 80vw;
}
.flex__item {
flex-grow: var(--flexGrow);
inline-size: 10vw;
}
效果如下:
从该示例的代码中我们可以获知:
- Flex容器的宽度是
80vw(CSS中设置了inline-size: 80vw) - Flex容器中共有四个Flex项目,并且每个Flex项目的宽度是
10vw(CSS中设置了inline-size: 10vw) - Flex项目宽度总和为
10vw x 4 = 40vw - Flex容器的剩余空间为
80vw - 40vw = 40vw - Flex项目的
flex-grow的值分别是0、1、2和3,所有Flex项目的flex-grow总和为0 + 1 + 2 + 3 = 6
即:
flex-grow公式中变量名称 |
Flex项目1 | Flex项目2 | Flex项目3 | Flex项目4 | 总数 |
|---|---|---|---|---|---|
Flex项目的flex-grow值 |
0 |
1 |
2 |
3 |
0 + 1 + 2 + 3 = 6 |
Flex项目的宽度(width) |
10vw |
10vw |
10vw |
10vw |
10vw x 4 = 40vw |
Flex容器的宽度(width) |
80vw |
||||
| Flex容器的剩余空间 | 80vw - 40vw = 40vw |
||||
Flex项目新宽度(width) |
? |
? |
? |
? |
有了这些参数值之后,根据上面提供的flex-grow计算公式就可以计算出每个Flex项目的宽度:
» 计算之后的Flex项目宽度 = (Flex项目的flex-grow值 ÷ 所有Flex项目的flex-grow总值 × Flex容器剩余空间 ) + Flex项目初始宽度
» 计算之后的Flex1项目宽度 = (0 ÷ 6 × 40 ) + 10 = 10vw
» 计算之后的Flex2项目宽度 = (1 ÷ 6 × 40 ) + 10 = 16.667vw
» 计算之后的Flex3项目宽度 = (2 ÷ 6 × 40 ) + 10 = 23.333vw
» 计算之后的Flex4项目宽度 = (3 ÷ 6 × 40 ) + 10 = 30vw
具体如下图所示:

上面的示例中flex-grow的取值都是正整数值。事实上,flex-grow还可以设置为小数值。在上例的基础上,将Flex项目的flex-grow值分别设置为0、 0.1、0.2和0.3,此时所有Flex项目的flex-grow总和为0.6。虽然取值为小数,但其计算方式是一样的,只不过所有flex-grow值的总和小于1时,Flex容器的剩余空间不会完全分配完(最终还会有剩余的空间)。换句话说,就该例而言,Flex项目只分完了Flex容器剩余空间的60%。最终的效果如下:
虽然在Flex项目中显式设置了flex-grow的值(大于或等于1)时,Flex项目会根据相应的比率得到Flex容器剩余空间,Flex项目宽度会得到扩展(变宽),但这并不是绝对的,如果Flex项目上显式设置了max-width或max-inline-size时,Flex项目会受到它们的限制。比如下面这个示例,我们显式在Flex项目中设置max-inline-size: 18vw,得到的效果如下:
在上面的示例中,为了更好的向大家演示flex-grow的计算,在代码中给Flex项目显式的设置了inline-size的值为10vw(相当于设置了flex-basis值)。后面我们会详细的介绍flex-basis的使用。从示例的效果中不难发现,flex-grow和inline-size(或flex-basis)是会相互影响的。这也令Flex项目计算变得复杂化了,尤其是加入min-width、min-inline-size、max-widt和max-inline-size会更为复杂。
为什么这么说呢?我们用一个示例来举例。比如说,不在Flex项目上显式的设置inline-size、width或flex-basis的值。根据我们所掌握的CSS知识可以得知,这个时候,Flex项目的大小会取决于其内容的大小(相当于inline-size或width设置了max-content)。
示例中代码很简单:
:root {
--flexGrow: 1;
}
.flex__container {
inline-size: 80vw;
}
.flex__item {
flex-grow: var(--flexGrow);
}
示例中的四个Flex项目的flex-flow的值都是相同的,即1。Flex容器的宽度设置的是80vw,具体渲染出来的容器宽度和浏览器视窗有关联,同时Flex项目并未显式指定具体宽度的属性,那么其宽度会根据自身内容来决定。为了更好的向大家演示,通过浏览器调试器,将对应元素的盒模型尺寸截图示意:

从上图可以得到:
- Flex容器的宽度是
804px - Flex项目的宽度分别是
43.36px、92.09px、140.83px和189.56px,所有Flex项目宽度的总和为465.84px - Flex容器的剩余宽度为
804px - 465.84px = 338.16px - 所有Flex项目的
flex-grow值为1
将相应的值套用到flex-grow的公式中,可以得到:
» 计算之后的Flex项目宽度 = (Flex项目的flex-grow值 ÷ 所有Flex项目的flex-grow总值 × Flex容器剩余空间 ) + Flex项目初始宽度
» 计算之后的Flex1项目宽度 = (1 ÷ 4 × 338.16) + 43.36 = 127.9px
» 计算之后的Flex2项目宽度 = (1 ÷ 4 × 338.16) + 92.09 = 176.63px
» 计算之后的Flex3项目宽度 = (1 ÷ 4 × 338.16) + 140.83 = 225.37px
» 计算之后的Flex4项目宽度 = (1 ÷ 4 × 338.16) + 189.56 = 274.1px
注意,不同的浏览器对小数处理有差异。
flex-shrink
flex-shrink和flex-grow有点类似,不同的是 flex-shrink是用来控制Flex项目的收缩因子。通过前面的内容我们知道,在Flexbox布局中,有的时候Flex项目宽度总和会超过Flex容器宽度,造成Flex项目溢出Flex容器。对于开发者而言,总是希望Flex项目能根据容器的空间来自动调整自身的大小。针对这种场景,flex-shrink就能起作用了。
简单地说,flex-shrink会将Flex容器不足的空间按收缩因子的比例分配到Flex项目上,这个时候Flex项目就会根据比例收缩宽度,以免Flex项目溢出Flex容器。
flex-shrink语法规则也很简单:
flex-shrink: <number>
flex-shrink只接收正数值(可以是正整数,也可以是正小数),其默认值是1。即 Flex容器没有足够空间容纳所有Flex项目时,Flex项目默认都会收缩。如果你不想让Flex项目进行收缩,可以显式设置flex-shrink的值为 0,此时Flex项目始终会保持原始的fit-content宽度。
在Flexbox布局模块中,在使用flex-shrink时也可以按下面的方式使用:
- 所有Flex项目设置相同的
flex-shrink值 - 每个Flex项目设置不同的
flex-shrink值
正如上例所示,在Flex项目中不做任何设置,Flex项目会按相同的比例收缩(因为flex-shrink的默认值是1);显式设置flex-shrink为0时,Flex项目不会收缩,将会溢出Flex容器;不同的Flex项目设置不同flex-shrink值时会按不同的比例收缩:

同样的,flex-shrink也有一套相应的计算公式:

flex-shrink计算公式看上去要比flex-grow复杂一些,过程也多一点,但只要你真正理解了,就不会再有这样的感觉了。同样用实例来向大家阐述,这样更易于理解flex-shrink是如何计算,以达到Flex项目根据收缩因子自动收缩(或者说分配Flex容器不足空间):
<!-- HTML -->
<div class="flex__container">
<div class="flex__item" style="--flexShrink: 0">flex-shrink:0</div>
<div class="flex__item" style="--flexShrink: 1">flex-shrink:1</div>
<div class="flex__item" style="--flexShrink: 2">flex-shrink:2</div>
<div class="flex__item" style="--flexShrink: 3">flex-shrink:3</div>
</div>
/* CSS */
:root {
--flexShrink: 0;
}
.flex__container {
inline-size: 40vw;
}
.flex__item {
flex-shrink: var(--flexShrink);
inline-size: 15vw;
}
效果如下:
从该示例的代码中我们可以获知:
- Flex容器的宽度是
40vw(CSS中设置了inline-size: 40vw) - Flex容器中共有四个Flex项目,并且每个Flex项目的宽度都是
15vw(CSS中设置了inline-size: 15vw) - Flex项目宽度总和为
15vw x 4 = 60vw - Flex容器的不足空间为
40vw - 60vw = -20vw - Flex项目的
flex-shrink的值分别是0、1、2和3,所有Flex项目的flex-shrink总和为0 + 1 + 2 + 3 = 6
即:
flex-shrink公式中变量名称 |
Flex项目1 | Flex项目2 | Flex项目3 | Flex项目4 | 总数 |
|---|---|---|---|---|---|
Flex项目的flex-shrink值 |
0 |
1 |
2 |
3 |
0 + 1 + 2 + 3 = 6 |
Flex项目的宽度(width) |
15vw |
15vw |
15vw |
15vw |
15vw x 4 = 60vw |
Flex容器的宽度(width) |
40vw |
||||
| Flex容器的剩余空间 | 40vw - 60vw = -20vw |
||||
Flex项目新宽度(width) |
? |
? |
? |
? |
有了这些参数值之后,根据上面提供的flex-shrink计算公式就可以计算 总收缩宽度 和 Flex项目收缩比例。 我们先来计算出总收缩宽度:
» 总收缩宽度 = ∑(Flex项目初始宽度 × Flex项目的flex-shrink值)
» 总收缩宽度 = (15vw × 0) + (15vw × 1) + (15vw × 2) + (15vw × 3) = 15vw × 6 = 90vw
有了总收缩宽度就可以计算出 Flex项目的收缩比例:
» Flex项目的收缩比例 = (Flex项目的初始宽度 × Flex项目的flex-shrink值) ÷ Flex项目总收缩宽度
» Flex项目1的收缩比例 = (15vw × 0) ÷ 90vw = 0
» Flex项目2的收缩比例 = (15vw × 1) ÷ 90vw = 0.1667
» Flex项目3的收缩比例 = (15vw × 2) ÷ 90vw = 0.3333
» Flex项目4的收缩比例 = (15vw × 3) ÷ 90vw = 0.5
这样就可以计算出Flex项目的新宽度:
» Flex项目新宽度 = Flex项目初始宽度 - (Flex容器不足空间 × Flex项目收缩比例)
» Flex项目1新宽度 = 15vw - (20vw × 0) = 15vw
» Flex项目2新宽度 = 15vw - (20vw × 0.1667) = 11.666vw
» Flex项目3新宽度 = 15vw - (20vw × 0.3333) = 8.334vw
» Flex项目4新宽度 = 15vw - (20vw × 0.5) = 5vw
具体如下图所示:

其实flex-shrink还有另一套相对来说更为简单的计算公式:

根据上图的公式,计算出来的结果和前面计算的结果基本相等:
» Flex项目的新宽度 = Flex项目初始宽度 - (Flex容器不足空间 ÷ 所有Flex项目flex-shrink和) × Flex项目的flex-shrink
» Flex项目1的新宽度 = 15vw - (20vw ÷ 6) × 0 = 15vw
» Flex项目2的新宽度 = 15vw - (20vw ÷ 6) × 1 = 11.667vw
» Flex项目3的新宽度 = 15vw - (20vw ÷ 6) × 2 = 8.333vw
» Flex项目4的新宽度 = 15vw - (20vw ÷ 6) × 3 = 5vw
同样的,flex-shrink也可以取小数值,而且和flex-grow相似,如果所有Flex项目的flex-shrink总和小于1时,Flex容器的不足空间不会全部分完,Flex项目依旧会溢出Flex容器,比如下面这个示例:
另外,在Flexbox布局当中,会阻止Flex项目宽度缩小至0。如果某个Flex项目按照flex-shrink计算出来的新宽度趋向于0时,Flex项目将会按照该元素的min-content的大小来设置宽度,同时这个宽度将会转嫁到其他Flex项目,再按相应的收缩因子进行收缩。
在这个示例中,第四个Flex项目的flex-shrink的值为9。根据上面提供的公式,可以获知:
» Flex项目的新宽度 = Flex项目初始宽度 - (Flex容器不足空间 ÷ 所有Flex项目flex-shrink和) × Flex项目的flex-shrink
» Flex项目4的新宽度 = 15vw - (20vw ÷ 12) × 9 = 0
计算出来的宽度为0,但实际上这个时候渲染出来的宽度是该项目的min-content(该示例就是“shrink”单词的宽度),大约47.95px(约3.66vw)。那么这个值将会分成3份(因为该例另外三个Flex项目的flex-shrink是0、1和2),并且对应的Flex项目会继续分配本应Flex项目4要收缩的宽度。即:
» Flex项目1新宽度 = 15vw - 20 ÷ 12 × 0 - 3.66 ÷ 3 × 0 = 15vw (约196.5px)
» Flex项目2新宽度 = 15vw - 20 ÷ 12 × 1 - 3.66 ÷ 3 × 1 = 12.113vw (约158.6847px)
» Flex项目3新宽度 = 15vw - 20 ÷ 12 × 2 - 3.66 ÷ 3 × 2 = 9.227vw (约120.869px)
浏览器视窗宽度在1310px状态下渲染出来的结果如下:

在Flexbox项目中,虽然flex-shrink会让Flex项目根据收缩因子缩小自身宽度,但是Flex项目自身显式设置了min-width或min-inline-size时,计算出来的值要是小于min-width(或min-inline-size),Flex项目将会按min-width或min-inline-size的值渲染:
这个时候,设置了min-width(或min-inline-size)的Flex项目会类似于上例中的min-content一样,将未分配的Flex容器不足空间转嫁到其他的Flex项目上。
相对而言,
flex-shrink的计算要比flex-grow复杂一些,特别是当Flex项目根据flex-shrink计算出来的宽度趋于0时,会让计算变得复杂化。
在Flexbox布局模块中,基于前面提到的Flex容器的对齐属性、Flex项目中的flex-shrink和flex-grow我们就可以很好的处理Flex容器的剩余空间和不足空间:
- Flex容器有剩余空间(所有Flex项目的宽度总和小于Flex容器的宽度),如果设置了
flex-grow,Flex项目会根据扩展因子分配Flex容器剩余空间;在未设置flex-grow时,在Flex容器中是否设置了对齐方式,如果是,那么会按对齐方式分配Flex容器剩余空间,如果不是,Flex容器剩余空间不变 - Flex容器有不足空间(所有Flex项目的宽度总和大于Flex容器的宽度),如果设置了
flex-shrink值为0,Flex项目不会收缩,Flex项目溢出Flex容器;如果未显式设置flex-shrink值,Flex项目分平均分配Flex容器不足空间,Flex项目会变窄(Flex项目的flex-shrink的默认值为1),如果显式设置了flex-shrink的值为非0的不同值,那么Flex项目会按照不同的收缩因子分配Flex容器不足空间,Flex项目同样会变窄
具体的我们可以绘制一张这方面的流程图:

flex-basis
在Flexbox布局模块中,除了我们熟悉的width、min-width、max-width以及逻辑属性inline-size、min-inline-size和max-inline-size可以用来设置Flex项目的初始宽度之外,还可以使用flex-basis属性,即 在任何Flex容器空间(剩余空间或不足空间)分配发生之前初始化Flex项目尺寸。其语法很简单:
flex-basis: content | <width>
其默认值为auto。其中content是指Flex项目内容的自动尺寸,而<width>是我们熟悉的带有数值和单位的值,比如10vw、50%、200px等;除此之外,还可以是一些用来描述宽度的关键词,比如fill、max-content、min-content和fit-content()、inherit、initial和unset等。
如果flex-basis的值设置为auto,浏览器将先检查Flex项目的主尺寸是否设置了绝对值,再计算Flex项目的初始值。比如说,你给Flex项目显式设置了width: 200px,那么200px就是Flex项目的flex-basis值。
如果你的Flex项目可以自动调整大小,则auto会解析为其内容的大小,这个时候min-content和max-content会起作用,并且Flex项目的max-content作为flex-basis的值。
相比flex-grow和flex-shrink而言,flex-basis是最复杂的部分。事实上,在Flexbox布局模块中 设置Flex项目的尺寸大小存在一个隐式的公式:
content➜width➜flex-basis
简单地说,如果Flex项目未显式指定flex-basis的值,那么flex-basis将回退到width(或inline-size)属性;如果未显式指定width(或inline-size)属性的值,那么flex-basis将回退到基于Flex项目内容计算宽度。不过,决定Flex项目尺寸大小,还受flex-grow和flex-shrink以及Flex容器大小的影响。而且Flex项目 最终尺寸 会受min-width、max-width(或min-inline-size、max-inline-size)属性限制。这一点必须得注意。
接下来用一个简单的示例来阐述content、width以及flex-basis对Flex项目尺寸的影响。
其中
width是用来描述元素宽度的物理属性,从《图解CSS:CSS逻辑属性》一文中可以得知,与其相匹配的还有inline-size或block-size逻辑属性。注意,CSS逻辑属性受direction和writing-mode的值的影响。
示例代码很简单:
<!-- HTML -->
<div class="flex__container">
<div class="flex__item"></div>
<div class="flex__item"></div>
<div class="flex__item"></div>
<div class="flex__item"></div>
</div>
/* CSS */
.flex__container {
width: 600px;
display: flex;
border: 1px dashed #f36;
align-items: stretch;
}
按照上面的公式节奏来走,慢慢的加戏。
Flex项目不显式的设置任何与尺寸大小有关系属性,即用
content来撑开Flex项目。
在上面的HTML基础上添加内容:
<!-- HTML -->
<div class="flex__container">
<div class="flex__item">Lorem ipsum dolor sit amet</div>
<div class="flex__item">Lorem ipsum dolor sit amet consectetur adipisicing elit</div>
<div class="flex__item">Fugiat dolor nihil saepe. Nobis nihil minus similique hic quas mollitia.</div>
<div class="flex__item">Lorem ipsum dolor sit amet consectetur adipisicing elit. Molestias consequuntur sequi suscipit iure fuga ea!</div>
</div>
这个时候你看到的效果如下,而且每个Flex项目的宽度(以width为例)都依赖于元素内容的多少来控制:
注意,文本相关的CSS属性,比如font-size和font-family等属性此时会影响Flex项目尺寸的大小。
在这个示例中,并没有显式给Flex项目设置flex-basis属性,此时flex-basis会取默认值auto:

正如上图所示,浏览器将flex-basis属性的值以auto计算。此时 flex-basis的值会采用Flex项目内容计算出宽度:
- 第一个Flex项目计算出来的宽度值为
62.61px - 第二个Flex项目计算出来的宽度值为
126.55px - 第三个Flex项目计算出来的宽度值为
161.06px - 第四个Flex项目计算出来的宽度值为
247.78px
接下来看第二步:
显式给Flex项目设置
width值。
给所有Flex项目设置width: 120px:
:root {
--width: 120px;
}
.flex__item {
width: var(--width);
}
效果如下:
这个时候所有Flex项目宽度都是相等的:

这个示例中,只是显式给所有Flex项目设置了width: 120px。浏览器计算出来的flex-basis值依旧为auto,但显式的设置了width: 120px,最终width属性的值决定了Flex项目的尺寸大小。
继续加码:
显式给Flex项目设置
flex-basis值,即Flex项目同时有width和flex-basis值。
为了更好的区分flex-basis和width,在示例中把flex-basis和width设置不同的值:
:root {
--width: 120px;
--flexBasis: 150px;
}
.flex__container {
width: 800px;
}
.flex__item {
width: var(--width);
flex-basis: var(--flexBasis);
}
效果如下:
在这个示例中,虽然在Flex项目同时显式设置了width和flex-basis,但Flex项目最终的尺寸大小采用了flex-basis的值:

也就是说,同时显式给Flex项目设置flex-basis和width的值,则width会被忽略,不再起作用,相当于没有设置。
在该示例中,所有Flex项目宽度的总和是 600px,小于Flex容器宽度 800px,Flex容器的剩余宽度为200px:

如果所有Flex项目宽度的总和大于Flex容器宽度会怎么样呢?我想你可能知道结果了,但这里还是花点时间再向大家印证一下。我们在上例的基础上将Flex项目的flex-basis值调整为300px:
:root {
--width: 120px;
--flexBasis: 300px;
}
效果如下:
当你没看到上面的渲染效果时,你是否会认为,Flex项目应该是取值flex-basis,即300px。Flex项目会溢出Flex容器:

如果你真的是这么的认为,那说明你没有仔细阅读前面有关于flex-shrink的内容。事实上,Flex项目的宽度并不是width的值150px,也不是flex-basis的值300px,而是一个被重新计算后的值 199.5px:

这就是Flexbox布局模块的强大之处,会有这样的结果都归功于flex-shrink属性。从前面的内容我们可以得知,当Flex容器有不足空间(该示例为200px),并且未显式设置flex-shrink属性,那么浏览器将会取其默认值1作为收缩因子,并且根据flex-shrink计算公式计算出每一个Flex项目应该在原有宽度300px上减去多少应该收缩的宽度:
» Flex项目的新宽度 = Flex项目初始宽度 - (Flex容器不足空间 ÷ 所有Flex项目flex-shrink和) × Flex项目的flex-shrink
» Flex项目的新宽度 = 300px - (402px ÷ 4) × 1 = 199.5px
注意,在该示例中,Flex容器设置了
1px的边框,计算出来Flex容器不足的空间为402px。
前面提到过,在Flexbox布局模块中影响Flex项目尺寸大小应该根据其隐式公式(即 content ➜ width ➜ flex-basis )来进行判断。如果要显式给Flex项目设置尺寸大小,其最佳方式是 使用flex-basis,而不是width(或inline-size)。
最后还有一点千万别忘记:
使用
flex-basis时会受min-width和max-width(或逻辑属性中min-inline-size或max-inline-size)的限制。
在详细阐述这一点之前,我们先来回忆一下。
在CSS中除了通过width(或height)显式给一个盒子定义尺寸之外,还可以通过min-width(或min-height)和max-width(或max-height)来定义盒子尺寸。如果拿width、min-width和max-width同时给一个盒子设置宽度时,具体取值为哪个?这是一个很有意思的问题。我们可以通过这三者之间的权重来取决:
- 元素的
width大于max-width时,元素的width等于max-width,即max-width能覆盖width - 元素的
width小于min-width时,元素的width等于min-width,即min-width能覆盖width - 当
min-width大于max-width时,min-width优先级将高于max-width
这个规则同样适用于inline-size、min-inline-size和max-inline-size。
同样的,当flex-basis、min-width和max-width同时出现时,也可以套用上面的规则,最终客户端会决定哪个值运用于Flex项目上。这就是为什么说 flex-basis会受制于min-width和max-width。 事实上,我们在介绍flex-grow和flex-shrink时也提到过,当flex-grow计算出来的值大于max-width值时,会取max-width的值;当flex-shrink计算出来的值小于min-width值时,会取min-width值。
如果Flex项目同时出现width、flex-basis和min-width时,具体的运算过程如下:
- 根据法则:
content➜width➜flex-basis,判断出运用于Flex项目的值,即flex-basis会运用于Flex项目 - 再根据法则:Flex项目的
width小于min-width时,Flex项目的width等于min-width,即min-width能覆盖width
这样一来,如果flex-basis小于min-width时,Flex项目的宽度会取值min-width,即min-width覆盖flex-basis。
如果Flex项目同时出现width、flex-basis和max-width时,具体的运算过程如下:
- 根据法则:
content➜width➜flex-basis,判断出运用于Flex项目的值,即flex-basis会运用于Flex项目 - 再根据法则:Flex项目的
width大于max-width时,Flex项目的width等于max-width,即max-width能覆盖width
这样一来,如果flex-basis大于max-width时,Flex项目的宽度会取值max-width,即max-width覆盖flex-basis。
如果Flex项目同时出现width、flex-basis、min-width和max-width时,会在上面的规则上增加新的一条规则来进行判断:
当
min-width大于max-width时,min-width优先级将高于max-width。
那么套用到Flex项目中:
flex-basis大于max-width,Flex项目的宽度等于max-width,即max-width能覆盖flex-basisflex-basis小于min-width时,Flex项目的宽度会取值min-width,即min-width覆盖flex-basis
由于min-width大于max-width时会取min-width,有了这个先取条件我们就可以将flex-basis和min-width做权重比较,即:flex-basis会取min-width。反过来,如果min-width小于max-width时则依旧会取max-width,同时要是flex-basis大于max-width就会取max-width。
如果你理解了的话,可以使用更简单的规则来决定用于Flex项目的尺寸。
首先根据
content➜width➜flex-basis来决定用哪个来决定用于Flex项目。如果Flex项目显式设置了flex-basis属性,则会忽略content和width。而且min-width是用来设置Flex项目的下限值;max-width是用来设置Flex项目的上限值。
虽然使用flex-basis显式设置Flex项目的尺寸优于width或height,但这仅是理想尺寸。因为在Flexbox布局中就算是你显式设置了flex-basis也不能保证这将是Flex项目的最终尺寸。那是 Flex项目放入Flex容器之后一切都将有可能改变。前面也提到过了,Flex项目的实际尺寸大小会被flex的另外两个属性flex-grow和flex-shrink的影响。简单地说,如果容器有足够多的空间,Flex项目会根据扩展因子扩展到足够填充Flex容器;反之,如果容器没有足够多的空间容纳Flex项目时,Flex项目会根据收缩因子缩小到足够被容纳在Flex容器中。
怎么设置Flex项目的基本大小
通过前面的学习,我们可以获知,对于Flex项目,显式设置flex-basis是一个最佳方案。但对于如何设置Flex项目的基本大小,我们可以围绕以下几点来进行思考:
flex-basis的值是auto?Flex项目显式的设置了宽度吗?如果设置了,Flex项目的大小将会基于设置的宽度flex-basis的值是auto还是content?如果是auto,Flex项目的大小为原始大小flex-basis的值是0的长度单位吗?如果是这样那这就是Flex项目的大小flex-basis的值是0呢? 如果是这样,则Flex项目的大小不在Flex容器空间分配计算的考虑之内
如果用简单的话来概括的话:
flex-basis可以判断出Flex容器是存在剩余空间,还是存在不足空间- 如果Flex容器存在剩余空间,
flex-grow可以按扩展因子来扩展Flex项目大小 - 如果Flex容器存在不足空间,
flex-shrink可以按收缩因子缩小Flex项目大小 - 如果Flex项目显式设置了
max-width值,即使Flex项目按照flex-grow扩展因子扩展了Flex项目尺寸,也不会大于max-width的值 - 如果Flex项目显式设置了
min-width值,即使Flex项目按照flex-shrink收缩因子收缩了Flex项目尺寸,也不会小于min-width的值