Flex布局杂谈

好几个月没写文章了,主要是工作之外还要挤出时间做技术分享实属不易。其实flex布局应该算是所有程序员的CSS入门第一课了,实现一些简单的行列布局无脑好用。但其实flex还是有一些东西可以讲的,可以帮助我们实现一些进阶一点的布局效果

理解最小内容宽度

最小内容宽度是CSS里绕不开的一个概念,CSS中有一个关键字min-content, 可以设置将宽度设置为这个关键字:

width: min-content

那么这个关键字代表什么呢?简单来说,对于中文而言吗,最小内容宽度就是一个汉字的宽度:

啤酒烧烤小龙虾
.food { width: min-content; }

如果是英语的话,那么就是句子中最长的单词的长度:

this is a very looooooong sentence
.food { width: min-content; background-color: aqua; }

image.png

可以看到这时候div的宽度变成looooooong的宽度了,理解了最小内容宽度,就能理解一些flex布局

然后是第三种情况,如果div中是一个图片,那么最小内容宽度就是图片的原始尺寸: 这里那我的头像举例:

.food { width: min-content; background-color: aqua; }

image.png

可以看到div的宽度为230px

min-width变化导致flex子项溢出

默认情况下,如果我们在div上不设置min-width,那么min-width默认就是0,可以用JS来证明这一点,还是以food这个div为例:


但是如果将food作为flex的一个子项,那么他的min-width就会变成auto,下面我们就验证这一点:

.flex-container { display: flex; } .food { width: min-content; background-color: aqua; }

image.png

这时计算出来的min-width就会变成当前flex子项的最小内容宽度,可以通过写一个demo来证明这一点:

loooooooooooooooogword
.box { width: 100px; height: 100px; background-color: aqua; display: flex; } .innerbox { flex-shrink: 1; }

如上图代码,box设置了flex布局,同时子元素显式设置了felx-shrink为1,当然也可以不设置,因为felx子项不设置任何属性的情况下felx-shrink默认就是为1,表现为当容器空间不够时自动收缩。这段代码中子项的内容为一个长单词,他的宽度超过100px。但是父元素被我设置为了宽度为100px,如果是按照我们理解的逻辑,子项的felx-shrink为1,那么子项应该收缩到100px才对,然而此时的情况是这样的:

可以看到浅蓝色的父元素盒子确实是100 * 100px

但是子元素的宽度依然是长单词的宽度:

可能有的朋友会觉得,这是因为我没有设置字符换行,但这和字符不换行不同,字符不换行只是因为字符的宽度超过了容器的宽度,字符跑到了容器外面:

还是相同的DOM结构,只是这次把flex去掉了,可以看到innerbox的宽度是正常的

此时设置overflow-wrap: break-word;可以实现换行:

其本质还是刚才说的,正常状态下min-width计算值为0,所以将尽管字符是溢出的,但是innerbox的大小可以正常缩放。所以设overflow-wrap: break-word正常换行。

但是在flex布局中,子项的min-width为auto,此时浏览器计算出的min-width变成了最小内容宽度。所以不管怎么缩小,最小内容宽度就是他的min-width,导致了子项溢出到容器外。

那么解决办法其实也很简单,直接将min-width设置为0就好了:

.innerbox {
    flex-shrink: 1;
    min-width:0;
    overflow-wrap: break-word;
}

此时子项不再溢出,也能正确换行了。

各种不同的flex简写:

flex:1

这应该是最最常见的flex布局简写了,以往面试还被问到过。代表了flex:1 1 0%,也就是既能扩展,也能收缩,但是flex-basis是0。当空间足够时,flex子项会等比例扩长。如果空间不够的情况下,flex子项倾向于收缩到最小内容宽度,分别对应了下面两种情况(汉字的最小内容宽度是一个字):

初始值flex:initial

当我们什么都不设置的时候就是flex:initial,也就是flex:0 1 auto。也就是说会收缩,但不会扩展,初始宽度是flex-basis为auto,意味着计算值为内容宽度。收缩时因为flex-basis各不相同,所以收缩的比例按照内容宽度的比例进行收缩:

上图比例为1 : 1 : 2 : 3,因为不会扩张,可以根据汉字的数量观察。 依然是按照内容尺寸的比例进行收缩,收缩后依然维持了1:1:2:3

flex: auto

与flex1有所不同,他是flex:1 1 auto的缩写,可以扩长和收缩,但是初始尺寸flex-basis为内容宽度,这就导致了无论扩张还是收缩都会按照flex-basis来等比例进行,我就不放图了,感兴趣的话可以自己试一下

其余的一些缩写

那么文章写到这,聪明的朋友应该已经看出规律了。flex-basis不仅决定了内容的初始宽度,当元素收缩和扩张时(这里指的是子项的flex-shrink或flex-grow都取相同的值),也会影响内容收缩和扩展的宽度,比如flex:0 为flex:0 1 0%, flex-basis为0,倾向于收缩到最小内容宽度,flex-grow为0说明不扩张,那么他的flex子项默认情况下的宽度就是为最小内容宽度,感性的来看就是一列文字竖着排;flex:none为felx:0 0 auto: 不扩张也不收缩,初始值为内容宽度。尺寸永远保持内容宽度不变。

尾声

flex布局需要一定的理解成本。对于flex-shrink和grow的理解是比较容易的,无非设置收缩和扩张的比例。对于flex-basis来说,无非是0和auto这两个特殊情况,一个是对应了最小内容宽度,一个是对应了内容宽度。

还有就是开头所讲的min-width变化,有可能会导致子项的溢出问题,有时候写代码需要比较谨慎的关注这一点


这是一个从 https://juejin.cn/post/7369052013151125556 下的原始话题分离的讨论话题