本节摘要:Flexbox 是 CSS 布局的一次革命。在它出现之前,"多个元素均匀分布""垂直居中""随屏幕伸缩"这些今天看来天经地义的需求,都需要各种 hack 才能实现。Flexbox 把它们变成了声明式的属性:容器上写对齐和方向,项目上写伸缩——几行代码,导航栏、卡片行、居中盒全部拿下。
完成本节后,你将能够:理解 Flex 容器、项目、主轴、交叉轴的概念模型;使用 justify-content 和 align-items 控制两条轴上的对齐;使用 flex-grow、flex-shrink、flex-basis 控制空间分配;实现导航栏、等宽卡片、圣杯布局、标签云等常见模式。
把一个元素设为 display: flex,它就变成了 Flex 容器,里面的直接子元素自动成为 Flex 项目。注意"直接"二字——孙子辈不参与,这和后面 Grid 的规则一致。
Flex 容器有两条轴:主轴(main axis)和交叉轴(cross axis)。默认情况下,主轴是水平方向(从左到右),交叉轴是垂直方向。flex-direction 属性可以改变轴的方向。
Flex 容器的主轴与三个项目
轴是 Flexbox 全部心智模型的地基:justify-content 管主轴,align-items 管交叉轴。先把轴认对,属性就不会张冠李戴。主轴一旦被 flex-direction: column 竖过来,这两个属性的"方向含义"也随之互换——垂直居中变成了 justify-content: center 的事。
.container { display: flex; flex-direction: row; /* 默认:主轴水平 */ /* flex-direction: column; 主轴垂直 */ /* flex-direction: row-reverse; 主轴水平反向 */ }
justify-content 控制项目在主轴上的分布方式。
.container { display: flex; justify-content: flex-start; /* 默认:靠起点 */ justify-content: flex-end; /* 靠终点 */ justify-content: center; /* 居中 */ justify-content: space-between; /* 首尾贴边,项目间等距 */ justify-content: space-around; /* 每个项目两侧等距(首尾空隙是中间的一半) */ justify-content: space-evenly; /* 所有间距完全相等 */ }
| 值 | 效果 | 典型用途 |
|---|---|---|
flex-start |
项目靠起点 | 默认行为 |
flex-end |
项目靠终点 | 右对齐按钮组 |
center |
项目居中 | 居中导航 |
space-between |
首尾贴边,中间等距 | Logo 靠左、菜单靠右 |
space-around |
每个项目两侧等距 | 图标均匀分布 |
space-evenly |
所有间距相等 | 等距分隔 |
space-between 和 space-around 的差别值得动手对比一次:三个项目的容器里,前者的首尾项目紧贴容器边缘,中间一个大空隙;后者首尾各留半个空隙,视觉上"松"一些。导航栏几乎总是 space-between——品牌和菜单要贴边。
align-items 控制项目在交叉轴上的对齐方式。
.container { display: flex; align-items: stretch; /* 默认:拉伸填满交叉轴 */ align-items: flex-start; /* 交叉轴起点对齐 */ align-items: flex-end; /* 交叉轴终点对齐 */ align-items: center; /* 交叉轴居中 */ align-items: baseline; /* 文字基线对齐 */ }
默认值 stretch 常被忽视却极有用:一行卡片不等高时,默认拉伸让它们齐平——卡片列表的"等高"效果是 Flex 白送的。baseline 则让不同字号的文字底对齐,做"图标加文字"的行时比 center 更讲究。
垂直居中的终极方案——这可能是 Flexbox 最广为人知的应用,曾经困扰一代人的难题如今两行搞定:
/* 让子元素在容器中完美居中 */ .center-box { display: flex; justify-content: center; /* 主轴居中 */ align-items: center; /* 交叉轴居中 */ height: 300px; /* 容器得有高度可谈 */ }
注意末尾的注释:居中的前提是容器有富余空间,height: 300px 或其他高度来源不可少——容器自己都塌成内容高了,居中就无从谈起。这是"居中不生效"排查的第一站。
单个项目还可以用 align-self 覆盖容器的 align-items,做"一行里某个元素特立独行"的效果,比如消息列表里自己的头像靠另一侧。
每个 Flex 项目可以独立设置自己在空间分配中的行为。
flex-basis 定义项目在分配多余空间之前的初始大小;flex-grow 定义当有剩余空间时,项目如何瓜分这些空间;flex-shrink 定义当空间不足时,项目如何收缩。
.item { flex-basis: 200px; /* 初始宽度 200px */ flex-grow: 0; /* 不放大 */ flex-shrink: 1; /* 允许缩小 */ } /* 简写 */ .item { flex: 0 0 200px; } /* 固定 200px,不放大不缩小 */ .item { flex: 1; } /* 等分剩余空间 */
flex: 1 是 flex: 1 1 0% 的简写——基准为零、均分剩余空间,所以多个 flex: 1 的项目永远等宽,不管内容多少。flex: auto 则是 flex: 1 1 auto——先按内容排,再分剩余空间,内容多的项目自然更宽。
grow 按权重分蛋糕,shrink 按权重还蛋糕
两端对齐的三段式布局把三者合起来用:
.triple { display: flex; align-items: center; gap: 12px; } .triple .lead { flex: 0 0 auto; } /* 头部按内容,不动 */ .triple .body { flex: 1; } /* 中间吃掉所有剩余空间 */ .triple .tail { flex: 0 0 auto; } /* 尾部按内容,不动 */
flex-wrap 决定项目排不下时怎么办:默认 nowrap 强行压缩(配合 flex-shrink 缩小项目);wrap 换行;wrap-reverse 反向换行。gap 属性则统一了项目间距,取代了"每个项目加 margin-right 再给最后一个去掉"的老套路。
.tag-cloud { display: flex; flex-wrap: wrap; gap: 8px; } .tag { padding: 4px 12px; background: #E3F2FD; border-radius: 16px; }
值得一提的是:Flex 容器里没有外边距合并(第 3 章的预告在此兑现),间距行为完全可预测——这是很多团队从浮动时代迁移到 Flex 后最直观的"幸福感"来源。
.navbar { display: flex; justify-content: space-between; align-items: center; padding: 0 24px; height: 60px; }
.card-list { display: flex; gap: 16px; } .card { flex: 1; /* 等分宽度,配合默认 stretch 等高 */ }
.page { display: flex; flex-direction: column; min-height: 100vh; } .main { display: flex; flex: 1; /* 吃掉头部页脚之外的剩余高度 */ } .sidebar { flex: 0 0 250px; } .content { flex: 1; }
.media { display: flex; gap: 12px; align-items: flex-start; } .media img { flex: 0 0 auto; } .media .info { flex: 1; }
💡 动手建议:用 Flexbox 给个人主页重做导航栏(品牌名加一组菜单链接,
space-between一行见效),再把作品卡片列表改成.card-list加flex: 1的等宽等高版。改完后故意删掉align-items: center,观察导航两端上下错开——这能帮你记住交叉轴属性的存在感。
⚠️
flex: 1是flex: 1 1 0%的简写,意味着"可以放大、可以缩小、初始宽度为零"。而flex: auto是flex: 1 1 auto,初始宽度为内容宽度。大多数情况下用flex: 1就够了,但如果你希望项目先按内容宽度排列再分配剩余空间,用flex: auto。另外提醒:项目上设了min-width后,空间不足时它不会再被压破下限——"缩不动了"的排查方向。

图:justify-content 六种对齐方式对比
flex 三件套的分配逻辑可以用一笔账讲透。假设容器宽 600px,三个项目基准宽度合计 400px,间距忽略不计。第一步看基准:三个项目按各自 basis 摆下后,剩余空间是 200px。第二步看 grow:若三者 grow 分别为一、二、一,权重和为四,每个单位分得 200 除以 4 即 50px——最终宽度分别是基准加 50、基准加 100、基准加 50。空间不足时方向反过来:若项目合计 700px 超出容器 100px,shrink 按同样权重"还账",谁的可压缩基数大谁多还。
理解这笔账,几个"怪现象"就有了答案。多个 flex: 1 的项目永远等宽,因为它们的基准都是零,剩余空间按相同权重均分;给某项设 flex: 2 它并不是"两倍宽于 flex: 1 的邻居",而是"多分一倍的剩余空间"——基准不为零时两者有微妙差别;默认的 flex-shrink: 1 意味着空间不够时大家默认一起缩,设成零就是"宁可溢出也不缩"——固定侧栏不想被压扁时就靠它。
还有一个工程上重要的边界:伸缩的是"主轴尺寸"。flex-direction: row 时 grow 分配的是宽度,高度由 align-items 与内容决定;换成 column 后一切对调——很多人在纵向布局里发现"flex: 1 不管用了",实际是忘了容器的总高度从哪来(外层没给高度,剩余空间无从谈起)。排查弹性问题先问两件事:基准是多少、剩余空间从哪来,答案基本就在这两问里。
问:gap 能替代项目间的 margin 吗? 在 Flex 和 Grid 容器里,能且应该。gap 只在项目之间生效,首尾不会多出间距,也不参与外边距合并那套老规则;margin 则要处理"最后一个去掉"的边界。唯一限制是 gap 不能按方向差异化到"每个项目不同"的程度——需要不规则间距时还是得用 margin 精细控制。
问:为什么我的 align-items: center 没效果? 三个高频原因:容器没有确定的高度,交叉轴上没有可居中的富余空间;项目自身被 align-self 覆盖了;把 justify-content 和 align-items 的方向记反了——主轴被改成 column 后,垂直居中换成了 justify-content 的职责。先确认轴,再确认空间,最后查覆盖。
问:一行排不下想换行,为什么项目被压扁了? 默认 flex-wrap: nowrap 不许换行,空间不足时靠 shrink 压缩项目硬塞。加 flex-wrap: wrap 后收缩停止、超出的项目换行。若项目设过 flex-shrink: 0 或固定宽度,nowrap 下则会直接溢出容器。
问:什么时候 Flex 不是好选择? 当布局同时要求"行与列都对齐"时——比如三行三列、跨行跨列的框架,用 Flex 得嵌套多层容器,行列间距还难以共享;这正是 Grid 的主场。另一个信号是"想按网格线放东西而不是按流排东西"。一维用 Flex、二维用 Grid,这条界线在实战里几乎不会失手。
学 Flexbox 收获的不只是几个属性,还有一种"对齐语言"。主轴与交叉轴的抽象、分布与对齐的解耦、权重的分配——这套词汇在界面工具(设计软件的布局约束)和其他 UI 框架里反复出现,几乎是行业通用的心智模型。你在 CSS 里练熟的 justify 与 align,迁移到任何排版系统都是同义词。
这种解耦设计的妙处值得咀嚼:传统做法里"让三个按钮均匀分布并垂直居中"是一连串技巧(外边距平摊、行高等于高度、定位微调),每个技巧只解决一个侧面;Flex 把它拆成两个独立声明——分布归 justify-content,对齐归 align-items——组合即所得。把纠缠的需求正交分解成独立的开关,是好的 API 设计范式,Flexbox 是前端世界里离我们最近、也最值得体会的范本。
实战里还有一条经验:复杂界面先画草图,再翻译成"容器嵌套树"。每个容器问三个问题——方向是行还是列、内容怎么分布、怎么对齐——答案就是该容器的三行 Flex 声明。这把"看着设计稿发呆"变成"逐容器填空",是新手到熟手之间最实用的一道工序。
再补一份速查式的"需求到属性"对照,日常开发可以直接按图索骥:要水平居中找 justify-content: center,垂直居中找 align-items: center,两端对齐找 space-between,等分空间给每个项目 flex: 1,固定侧栏配弹性主区用 flex: 0 0 宽度 加 flex: 1,项目贴容器底用 margin-top: auto,一行放不下要换行加 flex-wrap: wrap,统一间距用 gap。八条覆盖了 Flex 日常提问的绝大多数——建议把这份对照默写一遍,写不出来的条目回对应小节再做一次实验;也可以反过来,随机挑两条属性组合到一个测试容器上,预判效果再刷新验证,几轮下来反射就建立了。属性表可以查,但"看到一个效果能立刻想到属性"的反射,只能在手上练出来。
justify-content 管主轴,align-items 管交叉轴,方向随 flex-direction 互换。stretch 让一行项目等高;容器内部没有外边距合并,间距行为完全可预测。justify-content: center 加 align-items: center,前提是容器有高度。flex: 1 等分、flex: auto 按内容加分配、flex: 0 0 宽度 固定。gap 统一间距、flex-wrap: wrap 允许换行——标签云、卡片行的标准配置,两者常搭伙出场。一维布局的难题告破。下一节进入二维世界——CSS Grid 网格布局,用一段声明"画"出整个页面的框架,行与列从此都在一张图纸上说了算。