你真的了解CSS的flex-basis吗?
CSS的flex-basis是Flexbox属性flex的简写属性之一。大部分同学在很多时候都认为flex(当然也包括flex-basis)属性非常的简单,但事实上并非如此,如果你阅读过《聊聊Flexbox布局中的flex的演算法》和《深入理解 flex 布局以及计算》会发现这flex并不容易。在W3C规范中flex-basis是一个基准值,它接受width和height相同的值。如果你熟悉的话,width和height是隶属于**CSS Intrinsic & Extrinsic Sizing Module Level 3**规范,而且该规范除了width、height属性之外还有min-width(或min-height)、max-width(或max-height)、min-content、max-content和fit-content等。那么问题就来了,在Flexbox布局中,flex-basis和CSS Intrinsic & Extrinsic Sizing Module Level 3提到的各属性有何差异呢?这就是今天将要和大家一起探讨的。如果你感兴趣,欢迎继续往下阅读。
先了解一些概念
看到前面提到的各种属性是不是就有点犯晕。为了让大家更好的理解后续的将要聊到的内容,咱们有必要先花一些时间来了解一些概念,虽然这些概念不是最重要的,但是必要的。
flex-basis
flex-basis指定了Flex元素在主轴方向的初始大小,如果不使用box-sizing改变盒模型的话,这个属性就决定了Flex元素的内容盒子的大小。
在Flexbox中flex-basis指定的是主轴方向的初始值。简单地说,除了auto和content,flex-basis都以与水平书写模式中width相同的方式解析(除了width值设置为auto,flex-basis设置为content)。当然,Flexbox是纵向布局(即flex-direction取值为column时),flex-basis对应的值就和height相同。而且当书写模式改变时,相应的取值方式也会有所改变(这一部分我们会放到后面来介绍)。用张图来简单的描述一下:

从上图可以获取到下面相关的信息:
- 当
flex-direction取值为row时,flex-basis相当于width - 当
flex-direction取值为column时,flex-basis相当于height
当你把CSS的书写模式引入进来之后,那么
width和height不再是我们所说的width和height,他们将会变成inline-size和block-size。
当然,这些所说的相当(或者相似)也是片面的,因为flex-basis和width(或height)也是有所差异的。
CSS的尺寸属性
在CSS中定义元素尺寸的大小的属性有很多,大家较为熟悉的是width、height、min-width、min-height、max-width和max-height。这些属性的值除了我们熟悉的一些长度单位值之外,还有auto、none、min-content、max-content和fit-content()。事实上除了这些物理属性值之外,还可以是一些逻辑属性值,比如block-size、inline-size、max-block-size、max-inline-size、min-block-size和min-inline-size。其实物理属性和逻辑属性值是有相应的对应关系,只不过他们更和CSS书写模式结合更紧密。比如:
- 物理属性
width、height对应的逻辑属性是block-size或inline-size - 物理属性
min-width、min-height对应的的逻辑属性是min-block-size或min-inline-size - 物理属性
max-width、max-height对应的逻辑属性是max-block-size或max-inline-size
CSS尺寸属性值
CSS的神奇之处就是属性就有对应的属性值。就上面提到的用来设置尺寸的属性都可以使用下面之些值来设置。除了常见的属性值之外,还有一些新属性值。我们简单的一起来过一下:
<length-percentage>
使用<length>或<percent>指定元素容器的大小(这里暂时忽略box-sizing对其影响)。在适当的情况下,根据框的包含块的width、height来解析百分比(百分比的计算是个很有意思的东西,容器自身的width和height对使用百分比设计尺寸的元素有着直接的影响)。
如果给CSS尺寸属性取值为负值,将会是一个无效值(客户端一般会对无效值以忽略的方式来渲染)。
auto
如果给width或height设置值为auto时,那么容器的大小将会以容器内容来计算,会是一个自动大小。
如果给min-width或min-height设置值为auto,将会指定一个自动计算好的最小值。但有一点需要注意,如果相关布局模块没有另外定义,它将解析为0。为了向后兼容,对于所有display的值控制的盒子框,auto解析的值也将是0。还有如果不是块盒子,auto也会解析为0。
none
如果取值为none时,元素盒子的大小是没有任何限制。
min-content
如果指定了内联轴(Inline Axis),那么min-content对应的大小则是内联大小(Inline Size);否则将表现为属性的初始值。即固有的最小宽度。
如果用到CSS Grid布局中来表述的话,它代表单元格最小宽度,可以不让内容溢出单元格。除非是不可避免的。比如下面这个示意图,就是在minmax()函数中的最小值和最大值都设置min-content,也更好的说明了min-content和max-content的差异:

max-content
如果指定了内联轴,那么max-content对应的大小则是内联大小,否则将表现为属性的初始值。即固有的首选宽度。
同样的用CSS Grid来举例的话,max-content它代表了单元格**“最理想的大小”**。单元格最小的宽度围绕着它的内容。例如,如果单元格的内容是一个句子,理想的单元格的宽度是整个句子的长度,无论长度是多少,单元格的内容都不会断行。如下图所示:

使用CSS的
minmax()函数可以很好的阐述auto、min-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作为最大内容。
flex-basis在Flexbox中的作用
flex-basis属性在任何空间分配发生之前初始化Flex项目的尺寸。其实默认值是auto。如果flex-basis的值设置为auto,浏览器将先检查Flex项目的主尺寸是否设置了绝对值再计算Flex项目的初始值。比如说,你给Flex项目设置的width的200px,那么200px就是Flex项目的flex-basis值。
如果你的Flex项目可以自动调整大小,则auto会解析为其内容的大小,这个时候,min-content和max-content会起作用。此时将会把Flex项目的max-content作为flex-basis的值。
这样可能难于较好的理解透彻flex-basis。在Flexbox布局中设置Flex项目的尺寸大小时有一个隐式的公式存在:
content➜width➜flex-basis。
简单地说,如果未显式指定flex-basis的值,那么flex-basis将回退到width属性;如果未显式指定width,那么flex-basis将回到基于Flex项目内容的计算宽度。不过,决定Flex项目尺寸方面,还受flex的其他两个属性flex-grow和flex-shrink以及Flex项目容器大小的影响。而且Flex项目最终尺寸会受min-width和max-width属性限制。这一点必须得注意。
我们接下来用一个简单的示例来阐述content、width以及flex-basis对Flex项目的影响。假设我们有一个Flex容器.container,该容器的尺寸是80vw x 80vh,同时该容器包含了四个Flex项目div.item。用HTML结构来描述:
<!-- HTML -->
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
// CSS
.container {
width: 80vw;
height: 80vh;
display: flex;
align-items: stretch;
}
我们按照上面的公式节奏来走,慢慢的加戏。
Flex项目不显式的设置任何有尺寸大小有关系属性,即用
content来撑开Flex项目(计算属性)。
在上面的基础上添加内容:
<div class="container">
<div class="item">CSS</div>
<div class="item">Hello CSS!</div>
<div class="item">I lover CSS, CSS is awesome!</div>
<div class="item">CSS is awesome! CSS is simple, but she is not easy!</div>
</div>
这个时候你看到的效果如下,而且每个Flex项目的宽度(暂以width为例)都仅依赖于元素内容的多少来控制:

在这个示例中,没有显式给flex-basis设置值,此时它会取默认值auto,因此 flex-basis的值会采用Flex项目内容计算出来的宽度。
注意,上图中的Flex项目的width值和font-size值有关。接下来看第二步:
显式给Flex项目设置
width值。
比如我们在上面的示例中,显式的给.item设置下面这个样式:
.item {
width: 10vw;
}

你可以看到每个Flex项目的width都是相等的(都是10vw,由于vw是一个相对单位,在上面截图中,浏览器计算出来的宽度是168px):
在这个示例中,没有显式给flex-basis设置值,此时它会取默认值auto,因此 flex-basis的值会采用显式设置的width值(即10vw)。简单地说,设置的width等同于设置flex-basis。
接着在上面的示例继续加戏:
显式给Flex项目设置
flex-basis值(即,Flex项目有width和flex-basis)值。
为了更好的区分flex-basis和width,在接下来的示例中,把flex-basis和width设置不同的值:
.item {
width: 10vw;
flex-basis: 15vw;
}

从上图的对比效果来看,要是在Flex项目上同时显式设置width和flex-basis时,Flex项目最终宽度的尺寸会采用flex-basis的值:
从上面的示例中我们可以得知,同时显式给Flex项目设置flex-basis和width的值,则width属性会被忽略,不再起作用,相当于没有设置。
该示例我们四个Flex项目的值加起来小于容器.container,如果flex-basis的值加起来大于容器的宽度会出现什么情景呢?来简单的模拟一下,将.items的flex-basis设置为30vw(加起来是120vw,大于容器的80vw),这个时候浏览器渲染出来的结果如下:

为什么会这样呢?按理说Flex项目会撑出容器,但事实并没有如此。这就是Flexbox布局的强大之处,这一切功劳都应该归功于flex的另外两个属性:flex-grow和flex-shrink。由于我们并未显式的设置它们的值,所以它们会取默认值:
.item {
flex-grow: 0;
flex-shrink: 1;
}
也就是说,Flex项目的扩大因子是0,收缩因子是1。他们之间计算还是较为复杂的,如果你对这方面感兴趣的话,建议您花点时间阅读下面两篇文章:
小结一下,在Flexbox布局中影响Flex项目尺寸大小应该根据前面提到的公式(即 content ➜ width ➜ flex-basis )来进行判断。如果要显式给Flex项目设置尺寸大小,其最佳方式是 使用flex-basis,而不是width(或height)。
在Safari浏览器上还有一个bug: 如果要设置Flex项目的
height,不显式设置flex-basis的话,那么flex-shrink将不会生效。
最后还有一点千万别忘记,使用flex-basis时会受min-width和max-width的限制。有关于这方面的示例将放到下一节中来阐述。
CSS中的width
从CSS的盒模型中我们可以得知,在CSS的世界中任何一个元素都是一个盒子,只要是盒子就有大小,往往显式给盒子设置大小都是通过width和height来控制。随着CSS的越来越强大,给元素设置width(或者height)不在仅局限于我们熟知的CSS单位,比如px、%、rem、em、vw和vh等。
换句话说,能给width的值到目前为止有很多:
[ <length> | <percentage> ] && [ border-box | content-box ]? | available | min-content | max-content | fit-content | auto
简单的介绍一下之些值的含义:
<length>:可能的长度单位见<length><percentage>:指定为包含块宽度的<percentage>border-box:如出现, 之前的<length>或<percentage>应用到元素的边框盒子content-box:如出现, 之前的<length>或<percentage>应用到元素的内容盒子auto:浏览器将会为指定的元素计算并选择一个宽度fill:使用fill-available行内尺寸或者fill-available块级尺寸其中一种来作为合适的书写模式max-content:固有的首选宽度min-content:固有的最小宽度available:包含块的宽度减去水平margin,border和paddingfit-content: 固有的最小宽度或固有首选宽度(max-content)和可用宽度(available)的较小值
简单的用个示例来向大家演示:
在操作上面示例的时候,你可能看到很多值的效果和
width取值为auto相似。哪果希望彻底理解清楚之间的为什么,建议你花时间阅读W3C规范中的CSS Intrinsic & Extrinsic Sizing Module Level 3。
在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
在CSS中除了选择器有权重的概念,在一些属性上也会有权重的概念,就好比
width、min-width和max-width同时出现在一个元素上时,就需要看谁覆盖谁。小心哟,这也是一个经典的面试题,一不小心就你挂在这里了。(^_^)!
flex-basis 和 width的差异
前面提到过,在Flexbox布局中给Flex项目设置尺寸最佳的方式是使用flex-basis而不是width(或height)。换句话说,能用于width的值也可以用于flex-basis。比如上面示例,我们把width设置为一个固定值,你可以动态的将选择列表中的值赋值给flex-basis。查看取不同值的效果:
建议使用Firefox 或 Firefox Nightly 查看上面的Demo,效果更佳!
有一点需要特别的注意,在CSS中的同一个元素同时出现width、min-width和max-width来控制元素的宽度时会受三者权重关系的影响,最终将哪个属性的值运用于元素上。同样的,当flex-basis、min-width和max-width同时出现时,也可以套用这个权重,最终客户端会决定哪个值运用于元素上。这就是为什么 flex-basis会受制于min-width和max-width。
来看几个小示例。先来看flex-basis和min-width的结合:
.item {
flex-basis: 10vw;
min-width: 15vw;
}

在该示例中,.item同时出现了width、flex-basis和min-width。首先根据前面的法则一:
content➜width➜flex-basis。
简地说,该示例中.item的width会被忽略。接着我们再根据前面的法则二:
元素的
width小于min-width时,元素的width等于min-width,即**min-width能覆盖width**。
将该法则套到flex-basis中来:
如果
flex-basis小于min-width时,Flex项目的宽度会取值min-width,即min-width覆盖flex-basis。
因此,就该示例来说,最终运用Flex项目宽度的值是min-width,客户端计算出来的值是252px:
我们再来看一个flex-basis和max-width的示例。
.item {
flex-basis: 15vw;
max-width: 12vw;
}

同样的将前面的规则套进来,不同的是第二条规则换成:
元素的
width大于max-width时,元素的width等于max-width,即**max-width能覆盖width**
套到flex-basis中来。
flex-basis大于max-width,Flex项目的宽度等于max-width,即max-width能覆盖flex-basis。
所以最终用于Flex项目width的值是max-width,即12vw,客户端计算出来的值是201.594px:
同样的,如果在Flex项目中同时运用了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-basis- 如果
flex-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。
上面关系看上去有点乱乱的,但如果能一步一步将相应的规则套进来,则会很清晰,也会很简单。比如下面这个示例:
.item {
width: 10vw;
flex-basis: 12vw;
max-width: 15vw;
min-width: 18vw;
}
你可以看到最终用于Flex项目的则是min-width的值18vw,客户端计算出来的值是302.4px:
如果你理解了的话,可以使用更简单的规则来决定用于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-basis和width为例。事实上,当flex-direction取值为column(或column-reverse)时,flex-basis将会用来设置元素的height,而height和min-height以及max-height之间的权重等同于width、min-width和max-width之间的关系。
当flex-basis碰到逻辑属性时
如果你阅读过前面介绍CSS的display一文你会知道在现代的Web中定义盒子大小不再局限于width和height。特别是当你的业务面临着国际化的时候,和书写模式结合将会更为紧密,那么以前那些物理属性(width和height就属于物理属性)局限性会更大。为了改变这种局限性,CSS中引入了逻辑属性这样的重要概念和特性。
简单地说,我们不要在局限于x轴和y轴,应该用inline和block两个轴来表述更为准确:

对应的width和height随着书写模式的改变也会有所变化(宽可能不是宽,高可能不是高),具体的应该是inline-size和block-size:

来看一个示例,用物理属性和逻辑属性来描述一个元素大小:
尝试着改变下拉列表选项,你可以看到逻辑属性和物理属性的差异,如果你无法体会或者体会后你了解其中的原理,建议你阅读:
- 理解CSS的逻辑属性和值
- CSS逻辑属性和值(CSS Logical Properties and Values Level 1)
- CSS的逻辑属性对盒模型带来的变化
- CSS书写模式
- 使用
writing-mode实现垂直排版 display属性- CSS 盒模型
也就是说,如果我们用逻辑属性来替代物理属性的话,我们不在纠结于width(或height),而是用inline-size(或block-size)来替代。那么在Flexbox布局中,当flex-basis碰到书写模式将会是一个什么现象呢?接下来我们用两个示例简单的来看看。
首先我们在上面的示例做小小的改正。把Flex容器的物理属性换成逻辑属性,同时在Flex项目中显式指定flex-basis。如下面这个示例:
从上面的示例我们可以得出,如果flex-direction取值为row,并且writing-mode是horizontal-tb的话,那么flex-basis和inline-size相同(也就是我们常说的width);反之writing-mode取值是vertical-lr(或vertical-rl)那么flex-basis对应的是block-size(也就是我们常说的height)。
如果flex-direction取值为column时,那么writing-mode不同,flex-basis对应的值也不同:
是不是有点晕。要是有点晕的话,请继续撸码,亲自体验一下下。这样就不会再晕了。
小结
Flexbox布局已经是Web布局中最常用的布局方式之一了。大家都知道使用Flexbox布局能帮助我们更好的实现灵活的Web布局,特别是在移动端(大家都这么认为)。主要是因为Flexbox布局时,Flex项目会随着Flex容器大小自动的,较好的,灵活的实现适配。但往往很多同学却不知道为什么会这样?就好比我们今天所说的flex-basis这个属性。很多同学可能会说他只是Flex项目的一个基准值,那么他和width、min-width以及max-width有什么关系,估计就说不清楚了。甚至当flex-basis碰到了flex-grow和flex-shrink又有什么变化? 这些都是简单而又复杂的,需要我们好好去消化。
最后,希望大家通过阅读这篇文章能搞清楚flex-basis在Flexbox布局中怎么使用。