你真的了解CSS的flex-basis吗?

发布于 大漠

CSS的flex-basisFlexbox属性flex的简写属性之一。大部分同学在很多时候都认为flex(当然也包括flex-basis)属性非常的简单,但事实上并非如此,如果你阅读过《聊聊Flexbox布局中的flex的演算法》和《深入理解 flex 布局以及计算》会发现这flex并不容易。在W3C规范中flex-basis是一个基准值,它接受widthheight相同的值。如果你熟悉的话,widthheight是隶属于**CSS Intrinsic & Extrinsic Sizing Module Level 3**规范,而且该规范除了widthheight属性之外还有min-width(或min-height)、max-width(或max-height)、min-contentmax-contentfit-content等。那么问题就来了,在Flexbox布局中,flex-basisCSS Intrinsic & Extrinsic Sizing Module Level 3提到的各属性有何差异呢?这就是今天将要和大家一起探讨的。如果你感兴趣,欢迎继续往下阅读。

先了解一些概念

看到前面提到的各种属性是不是就有点犯晕。为了让大家更好的理解后续的将要聊到的内容,咱们有必要先花一些时间来了解一些概念,虽然这些概念不是最重要的,但是必要的。

flex-basis

flex-basis指定了Flex元素在主轴方向的初始大小,如果不使用box-sizing改变盒模型的话,这个属性就决定了Flex元素的内容盒子的大小。

在Flexbox中flex-basis指定的是主轴方向的初始值。简单地说,除了autocontentflex-basis都以与水平书写模式中width相同的方式解析(除了width值设置为autoflex-basis设置为content)。当然,Flexbox是纵向布局(即flex-direction取值为column时),flex-basis对应的值就和height相同。而且当书写模式改变时,相应的取值方式也会有所改变(这一部分我们会放到后面来介绍)。用张图来简单的描述一下:

从上图可以获取到下面相关的信息:

  • flex-direction取值为row时,flex-basis相当于width
  • flex-direction取值为column时,flex-basis相当于height

当你把CSS的书写模式引入进来之后,那么widthheight不再是我们所说的widthheight,他们将会变成inline-sizeblock-size

当然,这些所说的相当(或者相似)也是片面的,因为flex-basiswidth(或height)也是有所差异的。

CSS的尺寸属性

在CSS中定义元素尺寸的大小的属性有很多,大家较为熟悉的是widthheightmin-widthmin-heightmax-widthmax-height。这些属性的值除了我们熟悉的一些长度单位值之外,还有autononemin-contentmax-contentfit-content()。事实上除了这些物理属性值之外,还可以是一些逻辑属性值,比如block-sizeinline-sizemax-block-sizemax-inline-sizemin-block-sizemin-inline-size。其实物理属性和逻辑属性值是有相应的对应关系,只不过他们更和CSS书写模式结合更紧密。比如:

  • 物理属性widthheight对应的逻辑属性是block-sizeinline-size
  • 物理属性min-widthmin-height对应的的逻辑属性是min-block-sizemin-inline-size
  • 物理属性max-widthmax-height对应的逻辑属性是max-block-sizemax-inline-size

CSS尺寸属性值

CSS的神奇之处就是属性就有对应的属性值。就上面提到的用来设置尺寸的属性都可以使用下面之些值来设置。除了常见的属性值之外,还有一些新属性值。我们简单的一起来过一下:

<length-percentage>

使用<length><percent>指定元素容器的大小(这里暂时忽略box-sizing对其影响)。在适当的情况下,根据框的包含块的widthheight来解析百分比(百分比的计算是个很有意思的东西,容器自身的widthheight对使用百分比设计尺寸的元素有着直接的影响)。

如果给CSS尺寸属性取值为负值,将会是一个无效值(客户端一般会对无效值以忽略的方式来渲染)。

auto

如果给widthheight设置值为auto时,那么容器的大小将会以容器内容来计算,会是一个自动大小

如果给min-widthmin-height设置值为auto,将会指定一个自动计算好的最小值。但有一点需要注意,如果相关布局模块没有另外定义,它将解析为0。为了向后兼容,对于所有display的值控制的盒子框auto解析的值也将是0。还有如果不是块盒子,auto也会解析为0

none

如果取值为none时,元素盒子的大小是没有任何限制。

min-content

如果指定了内联轴(Inline Axis),那么min-content对应的大小则是内联大小(Inline Size);否则将表现为属性的初始值。即固有的最小宽度

如果用到CSS Grid布局中来表述的话,它代表单元格最小宽度,可以不让内容溢出单元格。除非是不可避免的。比如下面这个示意图,就是在minmax()函数中的最小值和最大值都设置min-content,也更好的说明了min-contentmax-content的差异:

max-content

如果指定了内联轴,那么max-content对应的大小则是内联大小,否则将表现为属性的初始值。即固有的首选宽度

同样的用CSS Grid来举例的话,max-content它代表了单元格**“最理想的大小”**。单元格最小的宽度围绕着它的内容。例如,如果单元格的内容是一个句子,理想的单元格的宽度是整个句子的长度,无论长度是多少,单元格的内容都不会断行。如下图所示:

使用CSS的minmax()函数可以很好的阐述automin-contentmax-content

fit-content()

fit-content()是CSS函数之一

如果显式指定了内联轴,使用了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项目设置的width200px,那么200px就是Flex项目的flex-basis值。

如果你的Flex项目可以自动调整大小,则auto会解析为其内容的大小,这个时候,min-contentmax-content会起作用。此时将会把Flex项目的max-content作为flex-basis的值。

这样可能难于较好的理解透彻flex-basis。在Flexbox布局中设置Flex项目的尺寸大小时有一个隐式的公式存在:

contentwidthflex-basis

简单地说,如果未显式指定flex-basis的值,那么flex-basis将回退到width属性;如果未显式指定width,那么flex-basis将回到基于Flex项目内容的计算宽度。不过,决定Flex项目尺寸方面,还受flex的其他两个属性flex-growflex-shrink以及Flex项目容器大小的影响。而且Flex项目最终尺寸会受min-widthmax-width属性限制。这一点必须得注意。

我们接下来用一个简单的示例来阐述contentwidth以及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项目有widthflex-basis)值

为了更好的区分flex-basiswidth,在接下来的示例中,把flex-basiswidth设置不同的值:

.item {
    width: 10vw;
    flex-basis: 15vw;
}

从上图的对比效果来看,要是在Flex项目上同时显式设置widthflex-basis时,Flex项目最终宽度的尺寸会采用flex-basis的值:

从上面的示例中我们可以得知,同时显式给Flex项目设置flex-basiswidth的值,则width属性会被忽略,不再起作用,相当于没有设置

该示例我们四个Flex项目的值加起来小于容器.container,如果flex-basis的值加起来大于容器的宽度会出现什么情景呢?来简单的模拟一下,将.itemsflex-basis设置为30vw(加起来是120vw,大于容器的80vw),这个时候浏览器渲染出来的结果如下:

为什么会这样呢?按理说Flex项目会撑出容器,但事实并没有如此。这就是Flexbox布局的强大之处,这一切功劳都应该归功于flex的另外两个属性:flex-growflex-shrink。由于我们并未显式的设置它们的值,所以它们会取默认值:

.item {
    flex-grow: 0;
    flex-shrink: 1;
}

也就是说,Flex项目的扩大因子是0,收缩因子是1。他们之间计算还是较为复杂的,如果你对这方面感兴趣的话,建议您花点时间阅读下面两篇文章:

小结一下,在Flexbox布局中影响Flex项目尺寸大小应该根据前面提到的公式(即 contentwidthflex-basis )来进行判断。如果要显式给Flex项目设置尺寸大小,其最佳方式是 使用flex-basis,而不是width(或height

在Safari浏览器上还有一个bug: 如果要设置Flex项目的height,不显式设置flex-basis的话,那么flex-shrink将不会生效

最后还有一点千万别忘记,使用flex-basis时会受min-widthmax-width的限制。有关于这方面的示例将放到下一节中来阐述。

CSS中的width

CSS的盒模型中我们可以得知,在CSS的世界中任何一个元素都是一个盒子,只要是盒子就有大小,往往显式给盒子设置大小都是通过widthheight来控制。随着CSS的越来越强大,给元素设置width(或者height)不在仅局限于我们熟知的CSS单位,比如px%rememvwvh等。

换句话说,能给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, borderpadding
  • 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)来定义盒子尺寸。如果拿widthmin-widthmax-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中除了选择器有权重的概念,在一些属性上也会有权重的概念,就好比widthmin-widthmax-width同时出现在一个元素上时,就需要看谁覆盖谁。小心哟,这也是一个经典的面试题,一不小心就你挂在这里了。(^_^)!

flex-basiswidth的差异

前面提到过,在Flexbox布局中给Flex项目设置尺寸最佳的方式是使用flex-basis而不是width(或height)。换句话说,能用于width的值也可以用于flex-basis。比如上面示例,我们把width设置为一个固定值,你可以动态的将选择列表中的值赋值给flex-basis。查看取不同值的效果:

建议使用Firefox 或 Firefox Nightly 查看上面的Demo,效果更佳!

有一点需要特别的注意,在CSS中的同一个元素同时出现widthmin-widthmax-width来控制元素的宽度时会受三者权重关系的影响,最终将哪个属性的值运用于元素上。同样的,当flex-basismin-widthmax-width同时出现时,也可以套用这个权重,最终客户端会决定哪个值运用于元素上。这就是为什么 flex-basis会受制于min-widthmax-width

来看几个小示例。先来看flex-basismin-width的结合:

.item {
    flex-basis: 10vw;
    min-width: 15vw;
}

在该示例中,.item同时出现了widthflex-basismin-width。首先根据前面的法则一:

contentwidthflex-basis

简地说,该示例中.itemwidth会被忽略。接着我们再根据前面的法则二:

元素的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-basismax-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-basismin-widthmax-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-basismin-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项目的尺寸。

首先根据contentwidthflex-basis来决定用哪个来决定用于Flex项目。如果Flex项目显式设置了flex-basis属性,则会忽略contentwidth。而且min-width是用来设置Flex项目的下限值;max-width是用来设置Flex项目的上限值

虽然使用flex-basis显式设置Flex项目的尺寸优于widthheight,但这仅是理想尺寸。因为在Flexbox布局中就算是你显式设置了flex-basis也不能保证这将是Flex项目的最终尺寸。那是Flex项目放入Flex容器之后一切都将有可能改变。前面也提到过了,Flex项目的实际尺寸大小会被flex的另外两个属性flex-growflex-shrink的影响。简单地说,如果容器有足够多的空间,Flex项目会根据扩展因子扩展到足够填充Flex容器;反之,如果容器没有足够多的空间容纳Flex项目时,Flex项目会根据收缩因子缩小到足够被容纳在Flex容器中

当然,这也是一个复杂的计算过程,只不过这里不做过多阐述,要是你对这方面感兴趣的话,建议花时间阅读:

上面我们拿的是flex-basiswidth为例。事实上,当flex-direction取值为column(或column-reverse)时,flex-basis将会用来设置元素的height,而heightmin-height以及max-height之间的权重等同于widthmin-widthmax-width之间的关系。

flex-basis碰到逻辑属性时

如果你阅读过前面介绍CSS的display一文你会知道在现代的Web中定义盒子大小不再局限于widthheight。特别是当你的业务面临着国际化的时候,和书写模式结合将会更为紧密,那么以前那些物理属性(widthheight就属于物理属性)局限性会更大。为了改变这种局限性,CSS中引入了逻辑属性这样的重要概念和特性

简单地说,我们不要在局限于x轴和y轴,应该用inlineblock两个轴来表述更为准确:

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

来看一个示例,用物理属性和逻辑属性来描述一个元素大小:

尝试着改变下拉列表选项,你可以看到逻辑属性和物理属性的差异,如果你无法体会或者体会后你了解其中的原理,建议你阅读:

也就是说,如果我们用逻辑属性来替代物理属性的话,我们不在纠结于width(或height),而是用inline-size(或block-size)来替代。那么在Flexbox布局中,当flex-basis碰到书写模式将会是一个什么现象呢?接下来我们用两个示例简单的来看看。

首先我们在上面的示例做小小的改正。把Flex容器的物理属性换成逻辑属性,同时在Flex项目中显式指定flex-basis。如下面这个示例:

从上面的示例我们可以得出,如果flex-direction取值为row,并且writing-modehorizontal-tb的话,那么flex-basisinline-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项目的一个基准值,那么他和widthmin-width以及max-width有什么关系,估计就说不清楚了。甚至当flex-basis碰到了flex-growflex-shrink又有什么变化? 这些都是简单而又复杂的,需要我们好好去消化。

最后,希望大家通过阅读这篇文章能搞清楚flex-basis在Flexbox布局中怎么使用。

扩展阅读