这篇文章对 flex 不熟也可以看。这篇文章只讲这三个属性。为了简单化,不会提到主轴交叉轴,也不讲方向,默认方向就是水平方向从左往右。但并不影响对这三个概念的理解。

# 1. 开启 flex

我们正常写三个 div。默认情况下是垂直排列的。只要开启 flex,一行代码就可以实现水平布局。这是 flex 最有用的一点。

<div id="box">
     <div class="div1 fontCenter">1</div>
     <div class="div2 fontCenter">2</div>
     <div class="div3 fontCenter">3</div>
</div>
#box {
    /*display: flex;*/
    height: 500px;
    width: 1000px;
    background-color: plum;
}
.div1{
    width: 150px;
    height: 150px;
    background-color: lightblue;
}
.div2{
    width: 150px;
    height: 150px;
    background-color: lightgreen;
}
.div3{
    width: 150px;
    height: 150px;
    background-color: lightsalmon;
}
.fontCenter{
    text-align: center;
    line-height: 150px;
    font-size: 50px;
}

在这里插入图片描述

# 1.1 使用 flex 实现水平排列

我们给容器原始 #box 加上 display: flex; 这行。表示开启 flex。这时候就已经实现了水平排列功能。非常的简单,这个功能用浮动来做就麻烦的多。

#box {
    display: flex;
    height: 500px;
    width: 1000px;
    background-color: plum;
}

在这里插入图片描述

# 1.2 左右分区

我想实现 box1 和 box2 在左边,box3 在右边。这个功能通过 flex 非常的好实现。只需要给 box3 设置 margin-left:auto 。box3 就会把能 margin-left 的都 margin 了。自己也就到了最右边,也就实现了我们想要的功能。

.div3{
    width: 150px;
    height: 150px;
    margin-left: auto;
    background-color: lightsalmon;
}

在这里插入图片描述
如果你想要把 box2 和 box3 一起移动到最右边,只需要把 margin-left:auto 设置在 box2 上面就可以了。

.div2{
    width: 150px;
    height: 150px;
    margin-left: auto;
    background-color: lightgreen;
}

在这里插入图片描述

# 2. 理解 flex-basis

flex-basis 这个属性是非常重要的一个属性,也是最容易混淆的一个属性。最开始以为和 width 属性差不多,也就没怎么重视。不弄清楚会造成和他相关属性的理解混乱。
其实 flex-basis 非常的好理解,你只需要逐字逐句的把他的定义理解一遍就行。

定义:
它的初始值是 auto,此时浏览器会检查元素是否设置了 width 属性值。如果有,则使用 width 的值作为 flex-basis 的值;如果没有,则用元素内容自身的大小。如果 flex-basis 的值不是 auto,width 属性会被忽略。

分为两种情况:

  1. 没有设置 flex-basis,这时候 flex-basis 为默认值 auto。
    这种情况很常见,就是只设置了 display:flex 的时候。别的 flex 属性都没设置。 此时浏览器会检查元素是否设置了 width 属性值。如果有,则使用 width 的值作为 flex-basis 的值;如果没有,则用元素内容自身的大小。 这句话暗含了一个很重要的信息就是在 flex 里面,flex-basis 是优先用于处理宽度的。更切确的说,在 flex 里面,根本不存在 width, 如果用户没设置 flex-basis 的值,flex 系统会把 width 的值赋值给 flex-basis。flex-basis 有了值后,width 被忽略,变成了工具人。
  2. 设置了 flex-basis 的值,这时候 width 会忽略,元素的宽度用 flex-basis 的值表示。
    这点就更加验证了在 flex 里面,flex-basis 是优先于 width 来处理宽度的。在 flex 里面只要用户设置了 flex-basis 的值,我 flex 系统都不正眼瞧你 width。

总之,在设置了 display:flex 之后,你最好设置 flex-basis 的值,并且忽略 width。 虽然 width 也是可以用,但就显得拖泥带水。就像你一只脚已经踏入我 flex 这个先进的布局系统里面,另一只脚还停留在原始的 CSS 里面。既然你已经用了 flex 这个系统,用就用全套,就用我 flex-basis 吧。而且我还可以和我另两个兄弟 flex-grow 和 flex-shrink 一起配合使用。组成 flex 三巨头。

虽然说 flex-basis 是替换了 width, 并且他和 width 起到的作用是差不多的。但他们还是不同的。
当 width 为 0 的时候,我们是看不到元素的。但当 flex-basis 为 0, 或者为 auto 并且 width 没有设置值的时候 (默认值为 0),该元素的大小是由内容大小决定的。也就是只要有内容,flex-basis 是不会看不到元素的。这在后面会从例子中体现。

设为 auto 和 0 也是有区别的。一个最明显的区别就是为 0 的时候,如果内容文字有空格是自动换行的。
这个可以通过设置 white space:nowrap 解决。auto 就没有这个问题。

例一:
div1 同时设置了 width 和 flex-basis,width 的值将会失效。

.box1{
    width: 150px;
    height: 150px;
    flex-basis: 200px;
    background-color: lightsalmon;
}
.box2{
    width: 150px;
    height: 150px;
    background-color: lightgreen;
}
.box3{
    width: 150px;
    height: 150px;
    background-color: lightblue;
}

宽度使用的是 flex-basis 的值而不是 width 的。
在这里插入图片描述

通常用法:
这里给父元素加了宽高和背景。

#content {
    display: flex;
    width: 1000px;
    height: 500px;
    background-color: lightpink;
}
.box1{
    height: 150px;
    flex-basis: 15%;
    background-color: lightsalmon;
}
.box2{
    height: 150px;
    flex-basis: 15%;
    background-color: lightgreen;
}
.box3{
    height: 150px;
    flex-basis: 20%;
    background-color: lightblue;
}

在这里插入图片描述

# 3. 理解 flex-grow

在设置了 flex-basis 后,元素就占用了一定的空间。但还留有一些空间。这些空间可以通过 flex-grow 来占领。
以上面的这种情况为例,box1,box2,box3 分别占据了父元素的 15%,15% 和 20% 的宽度,一共占据了 50% 宽度。还剩下 50% 的空白空间。
在这里插入图片描述
我们可以给 box 设置 flex-grow 属性。分别给三个 box 设置 flex-grow:1 。表示权重都是 1,那么三个 box 将会平分剩下的空间。

.box1{
    height: 150px;
    flex-basis: 15%;
    flex-grow: 1;
    background-color: lightsalmon;
}
.box2{
    height: 150px;
    flex-basis: 15%;
    flex-grow: 1;
    background-color: lightgreen;
}
.box3{
    height: 150px;
    flex-basis: 20%;
    flex-grow: 1;
    background-color: lightblue;
}

父元素的宽度是 1000px。
以 box1 为例,box1 原来的宽度是 1000*15%=150。grow 的宽度是 1000 * 50% * 1/3=500/3。所以 box1 最终宽度是 150+500/3。大家可以在开发者工具对比一下前后宽度就明白了。
在这里插入图片描述
如果子元素 flex-grow 的总和超过 1,那么就是按照上面的方式,按比例 (权重) 瓜分剩下的空间。
还存在一种情况是 flex-grow 的总和没有超过 1 的,比如出现小数的情况。

.box1{
    height: 150px;
    flex-basis: 15%;
    flex-grow: 0.3;
    background-color: lightsalmon;
}
.box2{
    height: 150px;
    flex-basis: 15%;
    flex-grow: 0.3;
    background-color: lightgreen;
}
.box3{
    height: 150px;
    flex-basis: 20%;
    flex-grow: 0.1;
    background-color: lightblue;
}

这种情况就不会完全瓜分剩下的全部空间。而是子元素将自己的 flex-grow 值和剩余空间相乘,计算出的值就是增加的值。以 box1 为例,剩余空间是 1000 * 50%=500,原来的宽度是 1000 * 0.2=200,500 * 0.3=150 这是增加的值,最后宽度是 200+150=350。
和总和超过一的区别就是,不是按照各个子元素直接的权重比例来分配了,而是直接用 flex-grow 这个值乘以剩余空间来获取增长的值。
在这里插入图片描述

# 3.1 flex-grow 的实际用途

其实上面提到的两种用法在实际开发中基本是用不上的。谁会给已经设置好宽度的元素又增加点宽度呢?实际上,下面这种用法才是最常见的。
下面这个效果在网页里面是经常用到的。左边是主要内容区,右边是一些小模块。使用 flex-grow 可以快速实现这样的功能。
我们想实现的效果是 box1:box2=3, 也就是 box1 宽度是 box2 宽度的 3 倍。
在这里插入图片描述

#content{
    display: flex;
    height: 700px;
    font-size: 50px;
}
.box1{
    height: 100%;
    flex-grow: 3;
    background-color: lightgreen;
}
.box2{
    height: 100%;
    flex-grow: 1;
    background-color: lightblue;
}
<div id="content">
    <div class="box1">box1</div>
    <div class="box2">box2</div>
</div>

但实际上,如果你打开开发者工具,测量一下,box1 和 box2 的宽度,你会发现两者比例根本不是 3:1。这是为什么?
也许你一开始就看出我这样写是有问题的,那么恭喜你,你对 flex 理解的很深刻。原因出在你没有设置 flex-basis:0 上。注意,一定要设置为 0,不写 flex-basis 默认值是 auto。为什么设置 flex-basis 为 0 就可以了呢?

我们来分析一下上面的代码的执行逻辑:
上面的代码我们没有设置 flex-basis,也没有设置 width。flex-basis 等于 auto,width 没有值。所以使用内容的宽度。 不信你把 flex-grow 属性都去掉,就会看到下面的效果。
这里引出 flex-basis 和 width 一个重要的区别。flex-basis 默认值是 auto, 并且没有设置 width 的时候,flex-basis 的值等于内容的宽度,值并不是为 0 的。这就是导致上面代码 box1 和 box2 不能实现 3:1 比例的原因。要想实现 box1 和 box2 比例是 3:1,box1 和 box2 的起始宽度必须是 0。这样 flex-grow 就可以按比例平分父元素空间了。
在这里插入图片描述
只需要给两个 box 都加上 flex-basis:0 就可以实现按比例平分的效果了。

.box1{
    height: 100%;
    flex-grow: 3;
    flex-basis: 0;
    background-color: lightgreen;
}
.box2{
    height: 100%;
    flex-grow: 1;
    flex-basis: 0;
    background-color: lightblue;
}

# 4. 理解 flex-shrink

在《深入理解 CSS》这本书里面,有下面这样一个图。给三个元素设置 flex-basis 都等于 40%。将会溢出一部分。
在这里插入图片描述
我也照着做了一遍。

<div id="content">
    <div class="box1">1</div>
    <div class="box2">2</div>
    <div class="box3">3</div>
</div>
#content{
    display: flex;
    height: 500px;
    width: 1000px;
    background-color: lightpink;
    font-size: 50px;
}
.box1{
    height: 200px;
    flex-basis: 40%;
    background-color: lightgreen;
}
.box2{
    height: 200px;
    flex-basis: 40%;
    background-color: lightblue;
}
.box3{
    height: 200px;
    flex-basis: 40%;
    background-color: lightsalmon;
}

发现和想象中的不一样。box3 并没有溢出。但三个 flex-basis 加起来确实是超出父元素宽度了。
但我心想,大牛写的书,应该不会骗我。一定是我写错了。但我没有写错,书里面说的也没错。
在这里插入图片描述
问题就出在 flex-shrink 身上。可是我也没写 flex-shrink 啊。你没写 flex 会自动给你添加上,而且默认值还不是 0,而是 1。
实际上,你上面的代码等价于下面的代码。

.box1{
    height: 200px;
    flex-basis: 40%;
    flex-shrink: 1;
    background-color: lightgreen;
}
.box2{
    height: 200px;
    flex-basis: 40%;
    flex-shrink: 1;
    background-color: lightblue;
}
.box3{
    height: 200px;
    flex-basis: 40%;
    flex-shrink: 1;
    background-color: lightsalmon;
}

flex-shrink 的作用和 flex-grow 相反。当合计的 flex-basis 的总宽度超过父元素的总宽度的时候。flex-shrink 会把子元素按比例减少,把溢出的宽度抹没。
比如前面的例子总宽度是 40% * 3=120%,也就是溢出了 20% 的宽度,也就是 1000 * 20%=200px 的宽度。怎么把这 200px 给抹没掉呢?就是从前面三个子元素上面扣宽度。三个子元素的 flex-shrink 都是 1,也就是平均扣除 200/3px。
就能实现抹没的效果。打开开发者工具,量一下子元素的宽度,确实是减少了这些宽度的。

如果说,我就想有这种溢出的效果,那么就把 flex-shrink 都设为 0 就好了。就可以达到下面这样效果。也就是书上提到的效果。
在这里插入图片描述

# 5.flex 属性的使用

介绍完了 flex-basis,flex-grow 和 flex-shrink 后,我们可以用他们的一种精简写法 flex。

例如:
我们给三个子元素都写上 flex:1 , 就可以实现三等分的效果。

.box1{
    height: 200px;
    flex: 1;
    background-color: lightgreen;
}
.box2{
    height: 200px;
    flex: 1;
    background-color: lightblue;
}
.box3{
    height: 200px;
    flex: 1;
    background-color: lightsalmon;
}

这个效果是真正三等分的。
在这里插入图片描述
这里的 flex:1 相当于下面的写法。三个值分别表示 flex-grow,flex-shrink,flex-basis。
注意, flex:1 和只写一个 flex-grow:1 是不一样的。
写成 flex:1 ,flex-shrink,flex-basis 的默认值是 1 和 0%。
而写成 flex-grow:1 ,flex-shrink,flex-basis 的默认值是 1 和 auto。也就是 flex-basis 是不同的。
而 flex-basis 值为 0 和 auto 的不同前面已经验证过了,就是 auto 在没有 width 的情况下宽度是内容的宽度。这会导致 flex-grow 不能整体平均分。具体见 flex-grow 小节。

flex:1 1 0%

或者

flex-grow:1
flex-shrink:1
flex-basis:0%

除了 flex-basis 会单独当类似 width 使用外。flex-grow,flex-shrink 基本是很少使用的。
flex-grow 也很少单独使用,因为使用 flex-grow 的时候,一般就是为了按比例分配布局。这时候要同时设置 flex-basis:0
所有一般使用下面的方式来替代单独使用 flex-grow。因为这种方式可以快速达到按比例分配布局。

flex:1

至于 flex-shrink 基本就不会用,也就设个默认值让子元素不会超出。

# 5.1 使用 flex 实现常用效果

  1. 包裹内容水平排列
    设置 flex 后,子元素也不要设置 width, 就会出现这种包裹内容水平排列的效果。
    在这里插入图片描述

  2. 第一个子元素固定,第二个子元素撑满剩余高度。
    flex-shrink 的值是无所谓的,因为并没有超出父容器宽度。
    在这里插入图片描述

  3. 三足鼎立型。两边固定宽度,中间填满剩余空间
    在这里插入图片描述

  4. 按比例划分在这里插入图片描述