你真的了解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
和padding
fit-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布局中怎么使用。