20

Oh My God,CSS flex-basis原来有这么多细节

 4 years ago
source link: https://www.zhangxinxu.com/wordpress/2019/12/css-flex-basis/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

byzhangxinxu from https://www.zhangxinxu.com/wordpress/?p=9154

本文欢迎分享与聚合,全文转载就不必了,尊重版权,圈子就这么大,若急用可以联系授权。

//zxx: 为了演示方便,避免无谓的干扰,本文所有的尺寸均指水平尺寸,对应CSS均是宽度相关,例如 width / min-width / max-width

以前的我对 flex-basis 不屑一顾,以为就是个浮于表面的CSS属性。

最近深入研究后才发现,自己那是朱砂当红土,珍珠当泥丸,钻石当玻璃,檀木当柴火——完全不识货!

在Flex布局中,一个Flex子项的宽度是由元素自身尺寸, flex-basis 设置的基础尺寸,以及外部填充( flex-grow )或收缩( flex-shrink )规则3者共同决定的。

本文只要讨论前两者对尺寸的表现影响,如果您对第3者对尺寸影响有兴趣,则可以参考“CSS flex属性深入理解”这篇文章。

直接进入正题。

一、flex-basis与盒模型

flex-basis 的尺寸是作用在 content-box 上的,这个和 width 属性是一样的。

例如:

.flex-basis {
    padding: 1em;
    border: 1em solid deepskyblue;
    color: deepskyblue;
    flex-basis: 100px;
}

.width {
    padding: 1em;
    border: 1em solid deepskyblue;
    color: deepskyblue;
    width: 100px;
}

在外部尺寸够大,内容尺寸够小的情况下,两者的表现是一样的。

如下图示意:

7ZZRJn3.png!web

我们可以通过设置 box-sizing 属性改变元素的盒模型,例如。

.flex-basis {
    padding: 1em;
    border: 1em solid deepskyblue;
    color: deepskyblue;
    flex-basis: 100px;
    box-sizing: border-box;
    word-break: break-all;
}

此时就可以看到内容展示宽度变小了,外部尺寸表现为 100px

7ZjMzu2.png!web

但是,在IE11浏览器下, box-sizing:border-box 是没有任何效果的。

zUr2aib.png!web

这个细节知识点还是有点用的,在开发内部系统的PC项目上,例如阅文内部系统,兼容Chrome和Firefox是没问题的,但顶不住老板用IE浏览器,因此,需要兼容到window 7 Edge,也就是IE 11,这个时候,就要避免改变Flex子项的 box-sizing 属性值,当然,移动端项目可以不用想那么多。

您可以狠狠地点击这里: flex-basic与盒模型样式表现demo

二、深入理解flex-basis和width的关系

大家一定要搞清楚这样一个事实, 在Flex布局中,子项设置width是没有直接效果的。

此时一定会有人反驳,我明明设置了 width:100px 就有效果啊!

对是有效果,但并不是 width 直接生效的,而是 flex-basis 的作用。

深入理解flex-basis:auto

flex-basis 的默认值是 auto ,表示自动,也就是完全根据子列表项自身尺寸渲染。

自身尺寸渲染优先级如下:

min-width > || max-width > width > Content Size

同时,在Flex布局中, flex-basis优先级是比width高的 (可以理解为覆盖)。

所以, flex-basiswidth 同时设置了具体的数值,则 width 属性值直接被打入冷宫,在样式表现上完全被忽略。

例如:

<by-zhangxinxu>
    <item-basis-width>项目1</item-basis-width>
    <item-basis-width>项目2</item-basis-width>
    <item-basis-width>项目3</item-basis-width>
    <item-basis-width>项目4</item-basis-width>
</by-zhangxinxu>

CSS如下:

by-zhangxinxu {
    display: flex;
}
item-basis-width {
    padding: 1em;
    border: 1px solid deepskyblue;
    color: deepskyblue;
    box-sizing: border-box;
    width: 200px;
    flex-basis: 100px;
}

结果 width:200px 完全是一个盒饭角色,所谓盒饭角色,就是明明参与了这部电视剧,也吃了剧组的盒饭,但是观众完全就没有意识到他的存在。这里的 width 属性就是这种性质,代码中确实出现过,但是样式渲染却完全体现不出来。

如下图所示,宽度表现为 100px

rMnIZz2.png!web

回到主线这里,实际上, flex-basis 值是 auto 的时候, width 属性值也是被打入冷宫的,只是,这个时候,由于后宫放权,所以,冷宫也是有影响力的。什么意思呢?

flex-basis:auto 的含义是,子项的基本尺寸根据其自身的尺寸决定。而这个自身尺寸与下面这几个方面有关:

  • box-sizing 盒模型(这个已经介绍过了);
  • width / min-width / max-width 等CSS属性设置;
  • content 内容(min-content最小宽度);

所以下面两段Flex布局中的CSS就不难理解了:

item-width {
    width: 100px;
}

最终尺寸 = 自身尺寸 mix basis尺寸。

此时,自身尺寸为 100px ,basis尺寸按照自身尺寸来算(因为此时属性值是 auto ),因此最终尺寸是 100px

item-basis {
    flex-basis: 100px;
}

最终尺寸 = 自身尺寸 mix basis尺寸。

此时,自身尺寸为content内容最小宽度,basis尺寸100px,因此最终尺寸是:如果content内容最小宽度不超过 100px ,则最终尺寸是 100px ,否则就是内容最小宽度。

记住上面的解释,我们再看下面几个例子,就会豁然开朗了。

案例1:相同表现

一个元素最小宽度超过100像素场景并不多,因此大部分场景下, flex-basiswidth 表现是一致的。

例如下面这个对比案例:

<h4>1. flex-basis:100px</h4>
<by-zhangxinxu>
    <item-basis>项目1</item-basis>
    <item-basis>项目2</item-basis>
    <item-basis>项目3</item-basis>
    <item-basis>项目4</item-basis>
</by-zhangxinxu>
<h4>2. width:100px</h4>
<by-zhangxinxu>
    <item-width>项目1</item-width>
    <item-width>项目2</item-width>
    <item-width>项目3</item-width>
    <item-width>项目4</item-width>
</by-zhangxinxu>

一个设置 width:100px ,一个设置 flex-basis:100px

item-width {
    width: 100px;
}
item-basis {
    flex-basis: 100px;
}

默认都是 100px 的展示,例如我的PC电脑显示器下:

nYFbIfZ.png!web

由于默认 flex-shrink 属性值是1,因此,容器宽度不足时候的弹性收缩效果也是一样的,如下GIF示意:

MzAZFvV.gif

眼见为实,您可以狠狠地点击这里: flex-basic与width的异同演示demo

案例2:不同表现

什么时候 width:100pxflex-basis:100px 表现不一样呢?就是最小内容宽度较大的时候,例如出现连续英文单词demo_by_zhangxinxu。

还是上面那个demo页面,点击任意某个子项,会改变元素里面的文字内容为demo_by_zhangxinxu。

此时就可以看到两者的差异了,如下图所示:

jEVJjuJ.png!web

因为此时content内容最小宽度超过了 100px ,于是 flex-basis:100px 按照了最小内容宽度显示了;但是 width:100px 把元素的尺寸限制得死死的,字符直接溢出容器之外。

要想两者表现一致,可以使用 word-break:break-all 使单词断开。

其实,本文一开始的“ flex-basic与盒模型样式表现 ”这个demo就已经展现了 flex-basiswidth 属性的区别了。例如第4项设置的 flex-basis 值是 100px ,同时 box-sizingborder-box ,但是最终表现出来的尺寸却不是 100px ,却是个更大的宽度值,如下图所示:

ieqEvue.png!web

主要是因为这一项里面的内容, 'basis:100px' 会当做一个独立的单词,并作为此时 flex-basis 默认就有的最小宽度,这个宽度值等同于 min-content 的计算尺寸。

我们可以对比一下隔壁 width:100px 的效果,可以看到文字内容 'width:100px' 直接溢出到了容器之外。

I7rmqaE.png!web

更深入的案例:最小内容宽度、width和flex-basis同时满足

根据上面的分析(不考虑容器尺寸不足或溢出),我们可以得到结论:

  • width:100px + flex-basis:auto = 元素自身100px
  • content + flex-basis:100px = max(content, flex-basis) = 大于等于100px

再加上 flex-basis优先级是比width高 的结论,我们就可以得到:

  • content + width:100px + flex-basis:100px = content + flex-basis:100px = max(content, flex-basis) = 大于等于100px

事实究竟是不是这样呢?我们看一个实际的例子。

CSS和HTML代码分别如下,还是使用我最爱的深天空蓝:

by-zhangxinxu {
    display: flex;    
}
item-zxx {
    padding: 1em;
    border: 1em solid deepskyblue;
    color: deepskyblue;
    box-sizing: border-box;
}
item-zxx:nth-child(1) {
    width: 100px;    
}
item-zxx:nth-child(2) {
    flex-basis:100px;
}
item-zxx:nth-child(3) {
    width: 100px;
    flex-basis: 100px;
}
<by-zhangxinxu>
    <item-zxx>我仅设置了width:100px,没有设置了flex-basis:100px。</item-zxx>
    <item-zxx>我没有设置width:100px,但是设置了flex-basis:100px。</item-zxx>
    <item-zxx>我设置了width:100px,同时设置了flex-basis:100px</item-zxx>
</by-zhangxinxu>

如果 flex-basis 优先级是比 width 高,则第3个子项的 width:100px 应该是忽略的,也就是项目2和项目3的渲染表现是一样的。

我们把上面的代码在各个浏览器下跑一下,结果发现出现了尴尬的事情:

在Chrome浏览器下,项目3和项目1表现是一样的,也就是 width:100px 依然起到了一些作用。

IjMvyiA.png!web

但是,在Firefox浏览器以及IE Edge浏览器下都是符合预期的项目3和项目2表现一样。

MnUbu22.png!webja6Bba6.png!web

如果想亲自确认一下,您可以狠狠的点击这里: flex-basic与width关系深入研究demo

下面问题来了,到底哪个正确哪个错误呢?

按照历史的经验,大概率Chrome浏览器会在N年之后的什么时候修改这个表现,和Firefox等浏览器一致。

至于谁对谁错啊,这个是没有定论的,规范中估计没有对这个细节进行特别详细的描述。

那给我们的启示是什么东西呢?那很简单, flex-basis数值属性值和width数值属性值不要同时使用 ,就不会遇到浏览器发现差异的问题。说的更极端一点,在Flex布局中,除非万不得已,否则没有使用 width 属性的理由,请使用 flex-basis 代替。

三、flex-basis还支持关键字属性值

以前以为 flex-basis 只支持数值,最近才发现, flex-basis 还支持一大波关键字属性值,包括:

/* 根据flex子项的内容自动调整大小 */
flex-basis: content;

/* 内部尺寸关键字 */
flex-basis: fill;
flex-basis: max-content;
flex-basis: min-content;
flex-basis: fit-content;

但是我要提前先泼一波冷水,目前,在Chrome浏览器下,上面任何一个关键字属性值都不支持。这是很罕见的场景。

其中, flex-basis:content 是Edge 12以及Firefox浏览器支持:

MVB3QnN.png!web

max-content / min-content 关键字目前仅Firefox浏览器支持。

YJBZF3b.png!web

fillfit-content 还没有任何浏览器支持。

由于最重要的Chrome浏览器不支持,因此上面几个关键字案例就不demo演示了,直接文字描述下他们的作用吧。

content 尺寸根据内容决定,跟我自己测试,表现和max-content接近。 max-content 最大内容宽度。 min-content 最小内容宽度。 fill 不详 fit-content 不详

四、flex-basis与min-width/max-width

很多前端在Flex布局的时候会使用 min-width 或者 max-width 限制列表上的最大尺寸和最小尺寸。

实际上很多时候都是没有必要的,活用 flex-basis 属性和 flex 属性,可以实现类似的效果。

例如我希望一个列表中的每一项最小宽度是100像素,尽可能填满整个容器,请问该如何实现?

无需使用 min-width 属性,可以试试下面的CSS代码:

.container {
    display: flex;
    flex-wrap: wrap;
}
.item {
    flex-basis: 100px;
    flex-grow: 1;
}

您可以狠狠地点击这里: flex-basic代替min/max-width效果实现demo

实现效果如下GIF示意:

yM3aU3v.gif

如果希望换行的列表也是等宽的,则需要复制N多空标签即可。

五、最后小结一下

本文内容最后的总结一下:

  • flex-basis 默认作用在content box上,IE11会忽略box-sizing;
  • width 只是 flex-basis 为auto时候间接生效,其余时候使用优先级更高的 flex-basis 属性值;
  • flex子项元素尺寸还与元素内容自身尺寸有关,即使设置了 flex-basis 属性值;
  • flex-basis 数值属性值和 width 数值属性值不要同时使用;
  • flex-basis 还支持很多关键字属性,只不过目前兼容性不太好;
  • flex-basis 使用得当可以实现类似 min-width / max-width 的效果。

关于Flex子项最终的尺寸也总结一下:是盒模型,元素自身尺寸特性,以及flex属性共同作用的结果。

想要彻底搞清楚啊,需要掌握一个体系的知识点,不是一件简单的事情。

本来想好好解释一下,一想到茫茫多啰里八嗦的东西,就算了,盒模型可以参考《CSS世界》,“元素自身尺寸特性”本文有提及,可以参考下,而要想了解flex属性三剑客的含义和作用可以参考文章以及上一篇文章“CSS flex属性深入理解”,总之,要学习和了解的东西很多。

行文匆忙,表达可能有些乱,有一些错误也在所难免,欢迎指正!

文中很多理解都属于个人理解,不一定准确,如果有不同意见,非常欢迎交流。

最后,感谢您阅读到这里,如果觉得文章写得还不错,欢迎转发或分享哦~

Fb2AZjZ.png!web

本文为原创文章,欢迎分享,勿全文转载,如果实在喜欢,可收藏,永不过期,且会及时更新知识点及修正错误,阅读体验也更好。

本文地址: https://www.zhangxinxu.com/wordpress/?p=9154

(本篇完)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK