当我们说flex布局时,我们在说什么,flex布局内的元素到底是怎么对齐的,大小是怎么样的,布局方式到底有什么规律?下面这篇文章将对于这些我们概念做一些简单的解释~
1.特点
首先,flex布局是专为一维内容设计的布局模型,它擅于获取大量不同大小的项,并为这些项返回最佳布局。跟浮动布局相比,flex布局的可预测性更好,还能提供更精细的控制。可以轻松解决之前困扰很久的垂直居中和等高列问题。
基本特点:
- 可以显示为行或列,但是如果想同时控制行列,那么估计很难办到,可以考虑使用grid布局
- 默认是单行,但是可以换行显示
- 在换行布局中的灵活度高
- ......
什么是等高列问题:就是一排元素各有各的高度,如果想要实现等高,调整起来十分不方便。比如例如,侧边栏和主内容区域需要视觉上等高,但内容量不同会导致高度差异。
2. 父盒子对齐方式
将一个盒子定义为flex弹性布局容器,它还定义了内部元素的展示方式。理解flex布局要理解这么几个概念,主轴,副轴(起点以及终点),换行以及水平垂直方向对齐方式
2.1 display: flex 与 display: inline-flex
使用flex布局,首先需要创建一个flex容器,使用flex布局,首先需要使用flex格式上下文,因为需要将display属性改成display:flex或者是display:inline-flex。接下来有两个问题,当我们写display:flex的时候,是什么意思,以及写完之后,发生了什么呢?
- 当我们写display属性的时候,我们实际上是在写什么呢?
display实际上是用来设置元素自身渲染的方式以及内部子元素的布局方式,所以display既可以设置自己的渲染方式,也可以控制子元素的对齐方式
所以实际上我们写的display:flex (完整写法应该是block flex) 而display: inline-flex 也可以写做inline flex
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.container1 {
display: flex;
background-color: pink;
}
.container2 {
display: inline-flex;
background-color: skyblue;
}
</style>
</head>
<body>
<div class="container1">
<div class="box">box1</div>
</div>
<span>111</span>
<div class="container2">
<div class="box">box2</div>
</div>
<span>222</span>
</body>
</html>

- 写完之后,发生了什么呢?
弹性项会立即开始使用其初始值来呈现一些 Flexbox 行为。
初始值包括一些:
- 默认一行显示
- 不会变大填满容器吧
- 在容器开头处对齐
2.2 主轴和交叉轴、换行
在对于父盒子的渲染设置了之后,下一步重要的就是设置如何排列子元素,那么首先理清楚这么几个概念就是主轴,交叉轴以及换行。这里不过多赘述:
- 主轴可以设置为flex-direction:row/column/row-reverse/column-reverse
- 与之相对的就是交叉轴
- 使用flex-wrap控制是否换行
- 简写 flex-grow: flex-direction flex-wrap

3.子元素宽度
内部子元素的宽度主要是使用flex属性来进行设置,这个属性也是困惑笔者很久的一个属性,接下来我就尝试对于这个属性理解的更深一点:
3.1 flex: initial
flex完整的设置是 👉 flex: flex-grow flex-shrink flex-basis
flex默认的值是initial,这个值是什么意思呢,对应上面的三个属性就是0 1 auto(不扩展,可收缩 默认宽度为auto)
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.container {
display: flex;
background-color: pink;
gap: 0.6rem;
}
.box {
/* flex: ; */
background-color: skyblue;
}
</style>
</head>
<body>
<div class="container">
<div class="box box1">box1</div>
<div class="box box2">box2</div>
<div class="box box3">box3</div>
<div class="box box4">box4</div>
<div class="box box5">box5</div>
</div>
</body>
</html>

-
当前宽度有剩余,所以不需要压缩,同时flex-grow:0也不需要伸展,默认的宽度是auto。那么这里的auto到底是什么意思?
- 如果我们给子元素设置width值,那么这个auto就会应用这个width值
- 如果没有width值,那么会根据单词的折词规则来设置 (大概类似下图)

-
如果说按照auto排布,当前的宽度不仅没有富余,还不够,那么又会如何处理呢?下面将结合具体的计算过程说明:
- 容器宽度: 300px
- 子元素宽度:200 + 100 + 100 + 100 + 100 = 600px
- 间隔:4 * 10 = 40px
- 实际总宽度:640px
- 👉 需要收缩的总空间 : 640 - 300 = 340 px
- 👉 总收缩权重:200 * 1(flex-shrink) + 100 * 1 * 4 = 600
- 最终宽度 = auto值 - 子元素收缩权重 / 总收缩权重 * 需要收缩的总空间
- 用box1举例:200 - 200 / 600 * 340 = 86.66666666666667
html<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <style> .container { display: flex; background-color: pink; width: 300px; gap: 10px; } .box { background-color: skyblue; } .box1 { width: 200px; } .box2, .box3, .box4, .box5 { width: 100px; } </style> </head> <body> <div class="container"> <div class="box box1">box1</div> <div class="box box2">box2</div> <div class="box box3">box3</div> <div class="box box4">box4</div> <div class="box box5">box5</div> </div> </body> </html>
可以看到实际情况和我们计算的结果一致,所以可以得出结论最终的宽度,是在flex-basis的基础上,综合flex-grow和flex-shrink计算得到的。
注意:启用换行后,子元素不再根据flex-shrink值收缩,任何超过弹性容器的子元素都会换行显示。
3.2 flex: auto
flex也可以设置别的值,如果flex:auto,实际上就相当于1 1 auto,可以看到和flex默认值最大的区别就是flex-grow从0变成了1
首先明确一点实际宽度不够,那么表现形式和上面是一样的,主要区别是有剩余空间的时候。之前是有剩余空间也不会扩展,现在是会扩展剩余空间,举个例子:
- 容器宽度: 300px
- 子元素宽度:40 + 50 * 4 = 240px
- 间隔:4 * 10 = 40px
- 实际总宽度:280px
- 👉 需要伸展的总空间 : 300 - 280 = 20 px
- 👉 总收缩权重:40 * 1(flex-grow) + 50 * 1 * 4 = 240
- 最终宽度 = auto值 + 子元素伸展权重 / 总伸展权重 * 需要伸展的总空间
用box1举例:40 + 40 / 240 * 20 = 43.3333
计算逻辑基本上和上面完全一致,当我们打开控制台可以看到一个向右拉伸的箭头,就表示伸展的宽度

3.3 flex: 1
可以看到上面的情况下,元素的宽度各不相同,因为它们都会基于flex-basis进行计算,那么如果我们希望所有项的宽度都均等分配呢?这么时候flex: 1就派上了用场,它的三个值分别是 1 1 0,就是默认大小都为0, 平分剩余的空间
在一行定宽,一行自适应中我们这样写:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.container {
display: flex;
background-color: pink;
width: 300px;
gap: 10px;
}
.box {
background-color: skyblue;
}
.box1 {
width: 50px;
}
.box2 {
flex: 1;
}
</style>
</head>
<body>
<div class="container">
<div class="box box1">box1</div>
<div class="box box2">box2</div>
</div>
</body>
</html>

👉 同理类比:flex: 2其实也就是flex: 2 1 0%
3.4 flex: none
它会提供不会伸缩或收缩的不可变 flex 项。如果只是使用 flexbox 来访问对齐属性,但不希望任何灵活的行为,这可能会很有用。(使用情况少,不过多赘述)
下面的这个图大概可以展示上面的逻辑,可以和上面的内容对照理解:

4.子元素排布方式
在理解了元素的宽度如何设置了之后,接下来我们思考如何有规律的排列这些元素呢?
4.1 justify-content
当子元素未填满容器时,justify-content属性控制子元素沿主轴方向的间距。它的值包括几个关键字:flex-start、flex-end、center、space-between以及space-around.
-
当子元素超过容器的时候,实际上这个值就不生效了
-
justify-content 的作用是分配未被 flex-grow 或主轴 margin: auto 消耗的剩余空间。如果存在以下任一情况:
- 任意子元素 flex-grow > 0(会优先扩展并消耗剩余空间);
- 任意子元素主轴方向 margin: auto(会优先吸收剩余空间);
- 则剩余空间会被这两种机制提前消耗,justify-content 无法再分配空间,因此 "失效"(不生效)。
(这里如何理解呢,我们举个例子解释一下⑧)
html
<style>
.container {
display: flex;
width: 500px;
height: 100px;
background: #f0f0f0;
justify-content: center;
gap: 10px;
}
.item {
width: 100px;
height: 50px;
background: skyblue;
/* flex-grow: 1; */
/* margin-left: auto; 情况2:取消注释后 justify-content 失效 */
}
</style>
<div class="container">
<div class="item">Item</div>
<div class="item">Item</div>
</div>



那么这里的margin-left: auto 是什么意思,或者在定位的时候,我们经常写的margin: auto到底在写啥呢?推荐看Everything About Auto in CSS 这篇文章,这里简单说一下就是:
比如div p等元素默认的width:auto, 就是占满父元素的宽度。但是height: auto就是实际文本的内容(为什么有这样的差异,实际上我们基本上只控制width,在垂直方向上习惯于让元素自然排布,这也就是为什么会出现前面说的等高列问题)
margin比较常见的用法就是使用margin: 0 auto(水平居中),以及在定位布局以及flex等布局中提供了一种对于元素更加精细的控制方式,比如这里的margin-left: auto

在这里我们给item单独设置了margin-left:auto, 可以看出它的表现形式完全不受justify-content的控制了,则将该方向的剩余空间全部分配给该 margin
详细可以看这个:W3C Flexbox 规范
4.2 align-items
align-items则控制子元素在副轴方向的对齐方式。align-items的初始值为stretch,在水平排列的情况下让所有子元素填充容器的高度。在垂直排列的情况下让子元素填充容器的宽度,因此它能实现等高列

注意: 取值是baseline的时候可以实现不同字号的内容对齐
4.3 align-content
如果开启了换行(用flex-wrap), align-content属性就可以控制弹性容器内沿副轴方向每行之间的间距,初始值stretch
4.4 align-self 以及 order
align-self 它能单独给弹性子元素设定不同的对齐方式
使用order属性能改变子元素排列的顺序
5.小记
实际上很早之前就接触到了这个属性,对于其中的细节一直比较模糊,但是平时又会经常遇到,自己每次简单记一下就觉得理解了,实际上还差的很远。这篇也是简单梳理,很多细节也不太详尽,如果有不足错误欢迎批评指正~