如果只能保留一个 CSS 布局模块,绝大多数前端工程师会毫不犹豫地选择 Flexbox。它把“让一个盒子居中”“让两栏自适应分配宽度”“让一行卡片自动换行”这些曾经需要各种 hack 才能完成的任务,变成了几行声明式代码。但 Flexbox 也是被误解最深的模块之一:很多人只会用 display: flex 加一个 justify-content: center,遇到 flex: 1 不生效、子元素溢出、align-content 没反应时就束手无策。本篇文章用 45 分钟的篇幅,从主轴与交叉轴的底层模型讲起,系统拆解 Flexbox 的每一个属性,配合多个可实时操作的实验台,帮你真正把 Flexbox 装进脑子里。
从 float 到 flex:布局的进化史
在 Flexbox 普及之前,前端工程师做布局靠的是三件套:float、display: inline-block 和 position: absolute。它们都能“凑”出效果,但没有一个是真正为布局而生的。
- float 的副作用:浮动元素脱离文档流,父容器高度塌陷,必须额外用
clearfix伪元素清除浮动。 - inline-block 的空白间隙:HTML 标签之间的换行符会被渲染成一个空格,导致元素之间出现约 4px 的神秘间隙,必须靠负 margin 或注释掉换行来消除。
- absolute 的失控:绝对定位完全脱离文档流,无法参与父容器的尺寸计算,响应式场景下几乎无法维护。
- 垂直居中:这是当年最著名的面试题,答案五花八门,但没有一个是干净的。
.center-old {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
/* 现在:一行搞定,无需知道元素尺寸 */
.center-new {
display: flex;
align-items: center;
justify-content: center;
}
/* 过去:两栏自适应 */
.col-left { float: left; width: 200px; }
.col-right { margin-left: 200px; }
/* 还要处理塌陷、清除浮动、盒模型…… */
/* 现在 */
.row { display: flex; }
.col-left { width: 200px; flex-shrink: 0; }
.col-right { flex: 1; }
Flexbox 到底解决了什么问题
Flexbox 的核心价值可以用一句话概括:它让“容器”拥有了在一条轴上分配空间、对齐子项的能力,而子项只需要声明自己的伸缩意愿。
- 空间分配:容器剩余多少空间、子项按什么比例瓜分,由
flex-grow与flex-shrink决定,不需要手动计算百分比。 - 对齐控制:主轴用
justify-content,交叉轴用align-items,语义清晰、互不干扰。 - 顺序无关:视觉顺序可以通过
order调整,不必修改 HTML 结构。 - 尺寸自适应:子项可以基于内容尺寸(
flex-basis: auto)或零基准(flex-basis: 0)参与分配。
主轴与交叉轴:理解 Flexbox 的唯一钥匙
初学者 90% 的困惑都来自一个根本原因:没有真正理解主轴(main axis)与交叉轴(cross axis)的关系。
Flexbox 是一个“方向敏感”的布局模型。当你写 display: flex 时,容器内部会凭空出现两条看不见的轴:
- 主轴:由
flex-direction决定方向,子项默认沿着主轴排列。主轴上的对齐由justify-content控制。 - 交叉轴:永远垂直于主轴。交叉轴上的对齐由
align-items/align-self/align-content控制。
最关键的一点是:这两条轴是动态的。当 flex-direction 从 row 变成 column 时,主轴变成垂直方向,交叉轴变成水平方向,而 justify-content 与 align-items 的作用方向也随之互换。这就是为什么很多人“明明写了 justify-content: center 却没有水平居中”——因为那一刻主轴是垂直的。
.container {
display: flex;
flex-direction: row; /* 主轴 → */
flex-direction: row-reverse; /* 主轴 ← */
flex-direction: column; /* 主轴 ↓ */
flex-direction: column-reverse; /* 主轴 ↑ */
}
/* 一句话记忆法 */
/* justify-content 永远作用于「主轴」 */
/* align-items 永远作用于「交叉轴」 */
/* 主轴方向变了,两者的视觉作用也跟着变 */
justify-content:主轴上的六种分布方式
justify-content 是使用频率最高的 Flexbox 属性,它决定了子项在主轴上如何分布剩余空间。它有六个取值,其中 space-around 与 space-evenly 的区别最容易混淆。
| 取值 | 行为 | 典型场景 |
|---|---|---|
flex-start |
全部靠主轴起点,剩余空间留在末端 | 默认状态、左对齐列表 |
flex-end |
全部靠主轴终点,剩余空间留在起点 | 右下角操作按钮组 |
center |
整体居中,两端剩余空间相等 | 居中标题、居中按钮组 |
space-between |
两端贴边,子项之间均分剩余空间 | 导航栏 logo 与菜单分离 |
space-around |
每个子项两侧各有一份相等的空间 | 等距图标排列 |
space-evenly |
所有间隙(含两端)完全相等 | 需要严格视觉等距的场景 |
space-around 与 space-evenly 的数学差异
假设容器剩余空间为 S,子项数量为 n:
space-between:间隙数量为n - 1,每个间隙 =S / (n - 1),两端为 0。space-around:每个子项两侧各分到S / (2n),相邻两个子项之间叠加后为S / n,而两端只有S / (2n)——这就是“两端是半个间隙”的由来。space-evenly:间隙数量为n + 1,每个间隙 =S / (n + 1),两端与中间完全一致。
space-around 期待“完美等距”。实际上它两端只有半个间隙,视觉上会显得内容“往里缩”。
space-evenly;需要两端贴边就用 space-between。
align-items 与 align-self:交叉轴上的对齐
align-items 作用于交叉轴,控制单行内所有子项的对齐方式。它的默认值是 stretch——这也是为什么很多人发现 flex 子项会“莫名其妙被拉满高度”。
| 取值 | 行为 | 注意点 |
|---|---|---|
stretch |
子项在交叉轴上拉伸填满容器 | 默认值;子项若有固定高度则不生效 |
flex-start |
靠交叉轴起点对齐 | row 方向时表现为顶部对齐 |
flex-end |
靠交叉轴终点对齐 | row 方向时表现为底部对齐 |
center |
在交叉轴上居中 | 与 justify-content: center 组合可实现完美居中 |
baseline |
按第一行文字的基线对齐 | 适合不同字号混排的场景 |
align-self:单个子项的例外
align-self 写在子项上,用来覆盖父容器的 align-items。它的取值比 align-items 多一个 auto,表示“继承父容器的设置”。
.row {
display: flex;
align-items: center;
}
.row .special {
align-self: flex-end;
}
/* 实用组合:头像顶部对齐、文字块居中 */
.media {
display: flex;
align-items: flex-start;
}
.media .avatar { flex-shrink: 0; }
.media .body { flex: 1; min-width: 0; }
flex-wrap 与 align-content:换行后才有意义
默认情况下,Flexbox 容器是单行的(flex-wrap: nowrap)。当子项总宽度超过容器时,它们不会换行,而是被强行压缩——如果设置了 flex-shrink: 0,就会直接溢出容器。
.container {
flex-flow: row wrap;
/* 等价于 */
flex-direction: row;
flex-wrap: wrap;
}
/* align-content 只在「多行」时生效 */
/* nowrap 或只有一行 → align-content 完全无效 */
.multi-line {
display: flex;
flex-wrap: wrap;
align-content: space-between;
min-height: 320px;
}
flex 简写:grow / shrink / basis 的三角关系
这是 Flexbox 中最重要、也最容易出错的一组属性。flex 是 flex-grow、flex-shrink、flex-basis 的简写,默认值是 0 1 auto。
三个属性的准确含义
flex-grow:默认0。当容器有剩余空间时,子项按 grow 值的比例瓜分。值为 0 表示不参与分配。flex-shrink:默认1。当容器空间不足时,子项按 shrink 值加权收缩。值为 0 表示绝不收缩,可能溢出容器。flex-basis:默认auto。子项在分配空间前的“基准尺寸”。auto表示使用元素自身尺寸(即width),0表示从零开始分配。
| 简写 | 等价于 | 效果 |
|---|---|---|
flex: 1 |
1 1 0% |
等分剩余空间,忽略内容宽度,最常用 |
flex: auto |
1 1 auto |
先按内容宽度,再等分剩余空间 |
flex: none |
0 0 auto |
完全不伸缩,保持自身尺寸 |
flex: 2 |
2 1 0% |
占据两份剩余空间 |
flex: 1 0 200px |
— | 基准 200px,可增长但不收缩 |
flex: initial |
0 1 auto |
默认值,不增长但可收缩 |
flex: 1 与 flex: auto 的真实差异
这是面试高频题,也是实际开发中经常踩的坑:
.item { flex: 1; }
/* 内容长短完全不影响最终宽度 */
/* 三个 flex:1 的子项 → 严格三等分 */
/* flex: auto —— 基准为内容宽度 */
.item { flex: auto; }
/* 内容长的子项最终会更宽 */
/* 剩余空间在内容宽度之上再等分 */
/* 验证:两个子项,内容宽度分别为 100px 与 300px */
/* 容器 800px,剩余 400px */
/* flex:1 → 各 400px(严格等分) */
/* flex:auto→ 300px 与 500px(含内容基准) */
flex: auto,结果内容多的那一列更宽,视觉上参差不齐。
flex: 1,配合 min-width: 0 防止长文本撑破布局。
flex-shrink 的加权收缩算法
很多人误以为 flex-shrink 是按值直接比例收缩,实际上浏览器使用的是加权收缩:
某子项收缩量 =
(该子项 shrink × 该子项 basis)
÷ Σ(所有子项 shrink × 各自 basis)
× 总溢出量
/* 这意味着:basis 越大的子项,收缩得越多 */
/* 即使它们的 shrink 值相同 */
/* 实用结论:不想让某个元素被压缩 */
.fixed { flex-shrink: 0; }
/* 想让文字区域吸收所有压缩 */
.text { flex: 1; min-width: 0; }
.icon { flex-shrink: 0; }
flex-basis 与 width:谁说了算?
当一个子项同时设置了 width 和 flex-basis 时,flex-basis 优先(除非它的值是 auto)。这是很多“我明明设了 width 却不生效”问题的根源。
.item {
width: 200px;
flex-basis: auto;
}
/* 结果:使用 width,即 200px */
/* 情况二:flex-basis 有明确值 */
.item {
width: 200px;
flex-basis: 300px;
}
/* 结果:使用 flex-basis,即 300px */
/* 情况三:flex-basis: 0 */
.item {
width: 200px;
flex-basis: 0;
flex-grow: 1;
}
/* 结果:width 被忽略,从 0 开始分配 */
/* 情况四:flex-basis: content */
.item {
flex-basis: content;
}
/* 结果:完全基于内容尺寸,忽略 width */
box-sizing 的影响
flex-basis 同样受 box-sizing 影响。在 border-box 下,basis 包含 padding 与 border;在 content-box 下则只是内容宽度。现代项目几乎都全局设置 box-sizing: border-box,但如果你在维护老项目,这一点值得留意。
order:不改 HTML 也能调整视觉顺序
order 属性接受一个整数(可以是负数),默认值为 0。子项按 order 值从小到大排列,值相同的保持 HTML 中的原始顺序。
order 只改变视觉顺序,不改变 DOM 顺序。键盘 Tab 键的焦点顺序、屏幕阅读器的朗读顺序仍按 HTML 结构走。用 order 把重要内容排到视觉前面,可能导致键盘用户“跳来跳去”。
gap:终于可以告别 margin 了
在 gap 出现之前,Flexbox 子项之间的间距只能靠 margin 实现,而 margin 会带来两个经典麻烦:最后一个子项多余的边距,以及换行后行间距的控制。
.list { display: flex; flex-wrap: wrap; }
.list .item { margin-right: 12px; margin-bottom: 12px; }
/* 问题:最后一个子项右侧多出 12px */
/* 问题:换行后的行间距难以精确控制 */
/* 问题:容器右侧出现一条无法消除的“空隙” */
/* 老式补救:负 margin 抵消 */
.list { margin: 0 -6px; }
.list .item { margin: 0 6px; }
/* 能解决,但会让父容器宽度溢出,容易引发横向滚动条 */
/* ✅ 现代方案:gap */
.list {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
/* 行列分别设置 */
.grid-ish {
display: flex;
flex-wrap: wrap;
row-gap: 20px;
column-gap: 12px;
}
gap 在动画中的额外好处
前面关于动画性能的文章提到过:margin 参与布局计算,修改它触发的重排代价很高;而 gap 同样参与布局,但它是容器级的单一属性,修改时浏览器只需要重新分配一次空间,比逐个修改子项 margin 高效得多。更重要的是,gap 不会影响子项自身的盒模型,做入场动画时不会出现“元素位移把邻居挤走”的现象。
margin: auto 的魔力:Flexbox 隐藏神技
在 Flexbox 中,margin: auto 有一个非常特殊的语义:它会吸收该方向上所有剩余空间。这意味着你可以用 margin 实现一些用 justify-content 做不到的效果。
.container { display: flex; height: 200px; }
.container .box { margin: auto; }
/* 等价于 justify-content + align-items 双居中 */
/* 技巧二:把某个子项推到最右 */
.navbar { display: flex; }
.navbar .logo { margin-right: auto; }
/* logo 右侧吸收所有剩余空间,菜单被推到最右 */
/* 技巧三:左右分组,中间留白 */
.toolbar { display: flex; }
.toolbar .left-group { margin-right: auto; }
/* 或者 */
.toolbar .right-group { margin-left: auto; }
/* 技巧四:多个 auto 平分剩余空间 */
.row .spacer { margin-left: auto; margin-right: auto; }
/* 两侧各吸收一半剩余空间,实现局部居中 */
为什么 margin: auto 能居中?
在块级布局中,margin: auto 只能吸收水平方向的剩余空间(因为垂直方向的剩余空间由内容高度决定,不会自动分配)。但在 Flexbox 中,子项的尺寸由 flex 算法决定,两个方向都可能存在剩余空间,因此 margin: auto 在两个方向上都生效。
六大经典布局模式,一次全部掌握
Flexbox 的威力体现在具体场景中。下面整理了六个最常用的布局模式,每一个都可以直接复制到项目里。
模式一:导航栏(Logo 左,菜单右)
display: flex;
align-items: center;
gap: 24px;
padding: 0 20px;
}
.navbar .logo {
margin-right: auto; /* 关键 */
}
.navbar .menu {
display: flex;
gap: 20px;
}
模式二:完美居中(水平 + 垂直)
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
}
/* 或者更简洁 */
.center > * { margin: auto; }
模式三:等宽卡片列表(自动换行)
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.card-list .card {
flex: 1 1 240px; /* 基准 240px,可增长可收缩 */
min-width: 0;
max-width: 100%;
}
/* 效果:屏幕越宽一行放越多,窄屏自动换行 */
模式四:圣杯布局(两侧固定,中间自适应)
display: flex;
gap: 20px;
min-height: 100vh;
}
.holy-grail .left, .holy-grail .right {
flex: 0 0 200px; /* 固定宽度,不收缩 */
}
.holy-grail .main {
flex: 1;
min-width: 0;
}
/* 移动端改为纵向堆叠 */
@media (max-width: 768px) {
.holy-grail { flex-direction: column; }
.holy-grail .left, .holy-grail .right { flex: 0 0 auto; }
}
模式五:粘性页脚(内容不足时页脚贴底)
display: flex;
flex-direction: column;
min-height: 100vh;
margin: 0;
}
main {
flex: 1; /* 主内容吸收剩余高度 */
}
模式六:媒体对象(头像 + 文字)
display: flex;
align-items: flex-start;
gap: 12px;
}
.media .avatar {
flex-shrink: 0;
width: 48px;
height: 48px;
border-radius: 50%;
}
.media .body {
flex: 1;
min-width: 0; /* 防止长文本撑破 */
}
六大经典陷阱与解决方案
Flexbox 的坑大多集中在“默认值”上。理解这些默认行为,能帮你省下大量调试时间。
陷阱一:min-width: auto 导致的溢出
这是 Flexbox 最著名的坑。flex 子项的 min-width 默认值是 auto,意味着它不会收缩到比内容更小。当一个子项里有长英文单词或 white-space: nowrap 的文本时,它会硬生生把容器撑破。
.row { display: flex; }
.row .text { flex: 1; }
/* 长文本会把 .text 撑得比容器还宽,出现横向滚动条 */
/* ✅ 解决方案 */
.row .text {
flex: 1;
min-width: 0; /* 关键!允许收缩到内容宽度以下 */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 同样的坑也存在于 flex-direction: column 时的 min-height */
.column-item { min-height: 0; }
陷阱二:flex-shrink 默认是 1
很多人希望某个元素保持固定宽度,却发现它总是被“挤扁”。原因是 flex-shrink 默认值为 1,所有子项都会参与收缩。
.icon { width: 24px; height: 24px; }
/* ✅ 明确声明不收缩 */
.icon {
flex-shrink: 0;
width: 24px;
height: 24px;
}
陷阱三:align-items: stretch 让子项高度失控
默认的 stretch 会让子项在交叉轴上拉伸。如果你希望子项保持自身高度,必须显式设置 align-items: flex-start,或者给子项设置固定高度。
陷阱四:百分比高度在 Flexbox 中失效
在 flex-direction: column 的容器中,如果容器没有明确高度,子项的 height: 100% 无法计算。解决方案是给容器设 min-height,或者用 flex: 1 让子项自动填充。
.sidebar { height: 100%; }
/* ✅ 用 flex 代替百分比 */
.layout { display: flex; min-height: 100vh; }
.sidebar { flex: 0 0 260px; }
.content { flex: 1; }
陷阱五:绝对定位的子项脱离 Flex 布局
当 flex 子项被设置为 position: absolute 时,它会脱离 flex 布局,不再参与空间分配。但它仍然受容器的 justify-content 与 align-items 影响——具体表现为它的静态位置由这两个属性决定。
陷阱六:嵌套 Flexbox 的尺寸传递
嵌套使用时,内层容器的尺寸由外层 flex 算法决定。如果外层没有给足空间,内层的 flex: 1 也无从发挥。此时需要在每一层都检查 min-width: 0 与 min-height: 0。
min-width: 0(列方向则是 min-height: 0)。
Flexbox 与 Grid:不是替代,而是互补
经常有人问“Flexbox 和 Grid 到底该用哪个”。答案是:它们解决的是不同维度的问题。
| 维度 | Flexbox | Grid |
|---|---|---|
| 布局维度 | 一维(行 或 列) | 二维(行 和 列) |
| 设计思路 | 内容驱动(由内容决定尺寸) | 布局驱动(先定网格再放内容) |
| 典型场景 | 导航栏、按钮组、卡片内布局、媒体对象 | 页面整体骨架、仪表盘、图片墙 |
| 子项数量未知 | 非常适合 | 需要定义轨道,略麻烦 |
| 对齐能力 | 强 | 更强(可跨行跨列) |
.page {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-rows: 64px 1fr 80px;
gap: 16px;
min-height: 100vh;
}
/* 卡片内部用 Flex */
.card {
display: flex;
flex-direction: column;
gap: 12px;
}
.card .footer {
margin-top: auto; /* 推到底部 */
}
/* 卡片列表:两者结合 */
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 16px;
}
Flexbox 的性能考量
Flexbox 本身性能优秀,但在大规模场景下仍有几个值得注意的点。
- 布局算法开销:Flexbox 需要两趟计算(先确定子项基准尺寸,再分配剩余空间),比块级布局略贵。但在现代浏览器中,这个差异只在数千个元素同时布局时才明显。
- 避免频繁触发重排:修改
flex-basis、flex-grow、width都会触发重排。做动画时请改用transform。 - 嵌套层数:过深的 Flexbox 嵌套会让布局计算呈指数增长。能用一层解决的,不要嵌套三层。
- gap 优于 margin:修改
gap只影响容器一次布局计算,而修改子项 margin 可能触发多次。 - contain 隔离:对独立的 flex 组件使用
contain: layout paint,可以把布局影响锁在局部。
/* ❌ 触发布局 */
.item:hover { flex-grow: 2; }
/* ✅ 只走合成 */
.item { transition: transform 0.25s ease; }
.item:hover { transform: scale(1.04); }
/* 给独立的 flex 组件加隔离 */
.toolbar, .card-row, .media {
contain: layout paint;
}
无障碍与浏览器兼容
Flexbox 的兼容性已经非常成熟,但无障碍方面有几个容易被忽略的细节。
无障碍注意事项
- order 与焦点顺序不一致:键盘 Tab 顺序遵循 DOM,而不是视觉顺序。用
order大幅调整位置时,键盘用户会感到困惑。 - flex-direction: *-reverse 同理:它只是视觉反转,DOM 顺序不变,屏幕阅读器仍按原顺序朗读。
- 不要用 Flexbox 做表格:表格数据应该用
<table>,Flexbox 没有行列语义,屏幕阅读器无法理解。 - 视觉顺序应尽量与 DOM 顺序一致:这是最简单也最有效的原则。
兼容性速查
/* flex-direction —— 全平台 */
/* justify-content —— 全平台 */
/* align-items —— 全平台 */
/* flex-wrap —— 全平台 */
/* flex 简写 —— 全平台 */
/* order —— 全平台 */
/* gap(Flexbox) —— Chrome 84+ / Safari 14.1+ */
/* space-evenly —— Chrome 60+ / Safari 11+ */
/* IE11 的已知问题(如需支持) */
/* 1. flex-basis 需要配合 width 使用 */
/* 2. flex 简写存在解析 bug,建议写全三个属性 */
/* 3. min-height 在 column 方向不生效 */
/* 4. 不支持 gap,需要用 margin 退化 */
/* 渐进增强写法 */
.list {
display: flex;
flex-wrap: wrap;
margin: -8px; /* 旧浏览器退化 */
gap: 16px; /* 现代浏览器覆盖 */
}
.list > * { margin: 8px; }
Flexbox 最佳实践清单
把全文的核心结论浓缩成一份可以贴在工位上的清单。
- 先想清楚主轴方向:决定用
justify-content还是align-items之前,先确认flex-direction。 - 等宽场景用 flex: 1,不要用 flex: auto:前者忽略内容宽度,严格等分。
- 固定宽度元素加 flex-shrink: 0:图标、头像、按钮最容易在收缩中变形。
- flex 子项默认加 min-width: 0:防止长文本或 nowrap 内容撑破容器。
- 间距优先用 gap:不再需要为最后一个子项写
:last-child。 - align-content 记得先开 flex-wrap:单行时它完全无效。
- 用 margin: auto 做特殊对齐:把某个子项推到一边,比嵌套容器更简洁。
- 不要动画 flex 属性:改动
flex-grow、flex-basis会触发布局,动画请用transform。 - order 只做视觉微调:涉及语义顺序时直接改 HTML。
- Grid 定骨架,Flex 做组件:二维用 Grid,一维用 Flex。
- 给独立组件加 contain:把布局与绘制的影响锁在局部。
- 移动端重新审视 flex-direction:横向排列在窄屏往往需要改成纵向。
- 用 DevTools 的 Flex 高亮检查:Chrome DevTools 可以在 Elements 面板实时可视化 flex 容器与轴线。
- 在真实设备上测试:模拟器无法反映真实的字体渲染与滚动行为。
/* 1. 全局:让 flex 子项可安全收缩 */
.flex-safe > * {
min-width: 0;
min-height: 0;
}
/* 2. 水平工具栏 */
.toolbar {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.toolbar .spacer { margin-left: auto; }
/* 3. 自适应卡片列表 */
.card-row {
display: flex;
flex-wrap: wrap;
gap: 16px;
contain: layout paint;
}
.card-row > * {
flex: 1 1 240px;
min-width: 0;
}
/* 4. 垂直堆叠 + 底部吸附 */
.stack {
display: flex;
flex-direction: column;
gap: 12px;
height: 100%;
}
.stack .footer { margin-top: auto; }
/* 5. 完美居中 */
.center-both {
display: flex;
align-items: center;
justify-content: center;
}
/* 6. 响应式降级:窄屏改为纵向 */
@media (max-width: 640px) {
.card-row, .toolbar {
flex-direction: column;
align-items: stretch;
}
}
Flexbox 的学习曲线其实很平缓,真正的门槛在于建立“轴”的思维模型。一旦你养成了“先确定主轴、再决定对齐”的习惯,绝大多数布局问题都会在几秒钟内找到答案。它不只是一个 CSS 属性集合,而是前端工程师思考布局方式的一次范式转变。
如果你在项目中遇到过更刁钻的 Flexbox 问题,欢迎回到上面的实验台,亲手改一改属性、看一看效果——那比读十篇文章都管用。