CSS Flexbox 详解:弹性布局模型的轴、对齐、尺寸与间距
2026-08-17
你在容器上写了 display: flex,三个盒子排成一行。然后 justify-content: center 生效了,align-items: center 却做出你完全没想到的效果;一个长单词把整个布局撑爆;子项还在你没要求的情况下神秘地变小了。Flexbox 是 CSS 里用得最多也最被误解的布局方式,因为它自带一套前所未有的心智模型——轴。一旦这个模型通了,整张属性表就顺理成章。本文从第一性原理把它讲透。
两根轴:主轴与交叉轴
每个 flex 布局都有两根轴,所有 flex 属性都定义在其中之一上。这一件事就能澄清大部分困惑:
- 主轴(main axis)——子项排列的方向,由
flex-direction决定。 - 交叉轴(cross axis)——与主轴垂直的方向。
flex-direction: row(默认值)让主轴水平——子项从左到右排成一排,交叉轴垂直。flex-direction: column 则互换:子项从上到下堆叠,交叉轴水平。
由此推出两个结论:
justify-content沿主轴分配子项。align-items沿交叉轴摆放子项。
这就是经典的混淆来源:人们因为单词里有 “align” 就以为 align-items 是水平居中。在 row 布局里它负责的是垂直方向。今后任何属性”不生效”,先问一句:它作用在哪根轴上,那是我想作用的轴吗?
容器与子项
display: flex 把这个元素变成 flex 容器,它的直接子元素变成 flex 子项。由此推出两件事:
- 只有直接子元素是 flex 子项。孙元素在父元素不是 flex 容器时仍是普通块级元素——所以真实布局要嵌套 flex 容器。
- 容器和子项的属性集合互不重叠。容器属性(
flex-direction、flex-wrap、justify-content、align-items、align-content、gap)负责排列;子项属性(flex-grow、flex-shrink、flex-basis、align-self、order、margin)控制子项自身的尺寸和位置。
主轴:方向、分布、换行、间距
方向。 flex-direction 取值 row、row-reverse、column、column-reverse。row 和 column 最常用;-reverse 变体镜像起点和终点(阿拉伯语布局和精细视觉排序时会用到)。
分布。 justify-content 决定主轴上剩余空间怎么分配:
flex-start/flex-end——子项挤向起点或终点(默认是flex-start)。center——聚在中间。space-between——两端无空隙,子项之间等距。space-around——每个子项两侧等距(两端各留半个间距)。space-evenly——处处等距,包括两端。
换行。 默认 flex-wrap: nowrap 把所有子项压在一行上,装不下就压缩或溢出。flex-wrap: wrap 让子项放不下时换到新行——这是响应式卡片行、徽章行的关键开关。
间距。 gap(配 row-gap 和 column-gap 分轴设置)定义子项之间的间距。它取代了老旧的 margin + nth-child 技巧,而且不像 margin 会在边缘重复叠加。
交叉轴:align-items、align-content、align-self
align-items 沿交叉轴摆放子项。它的默认值 stretch 是个坑:子项会拉伸填满容器的交叉尺寸,除非它自己的宽高(或 align-self)另有指定。其他取值:flex-start、flex-end、center、baseline(按文字基线对齐,适合不同高度的按钮行或表单标签)。
align-content 看起来是同类,但只在子项换行成多行时生效——它沿交叉轴分配的是行本身。只有一行子项时它什么都不做,这是”为什么没居中”的又一常见来源。
align-self 为单个子项覆盖 align-items——一排元素里想让某个掉到底部、其余居中时很好用。
子项尺寸:flex-grow、flex-shrink、flex-basis
三个子项属性决定主轴上每个子项的实际大小:
flex-basis——子项在缩放之前的初始主轴尺寸。默认auto取width/height或内容尺寸;0表示”从零开始,完全由 grow 份额决定”。flex-grow——所有子项按 basis 排完后还有剩余空间,就按 grow 值的比例分给各子项。默认0(不增长)。flex-shrink——basis 总和超出容器时,子项按 shrink 值的比例压缩。默认1。
默认的 flex-shrink: 1 就是”没让你缩你却缩了”的元凶:任何含固定宽度子项的 flex 容器在溢出时都会悄悄把它们压扁。两种停法:flex-shrink: 0,或者缩写 flex: none。
缩写把三者打包:flex: 1 等于 1 1 0%(等分增长、可压缩、忽略 basis),flex: auto 等于 1 1 auto(等分增长但尊重内容尺寸),flex: none 等于 0 0 auto(永不增减)。flex: 2 增长速度是 flex: 1 兄弟项的两倍。
还有一个坑:flex 子项默认(min-width: auto)拒绝缩到比最小内容尺寸还小。子项里的长单词或大图因此怎么缩都会溢出——解决办法是在子项上设 min-width: 0(或 overflow: hidden)。
经典错误
justify-content和align-items用反。 先问自己在哪根轴上。row 布局里水平是justify-content、垂直是align-items;column 布局两者对调。- “垂直居中不生效。” 你在 row 的交叉轴上居中——该用
align-items: center,不是justify-content: center。 - “为什么我的子项在缩小?” 默认
flex-shrink: 1。固定flex-shrink: 0或用flex: none。 - “文字溢出且修不好。”
min-width: auto阻止缩到内容宽度以下;给出问题的子项设min-width: 0。 - 用 margin 做间距。 用
gap,它就是为此而生的。 - 居中的一行代码。 flex 子项的
margin: auto会吸收全部剩余空间,所以单个margin: auto的子项会在两根轴上都居中——不需要任何 flex 属性的”完美居中按钮”经典技巧。
用嵌套 flex 搭建真实布局
flex 在小组合里最出彩。导航头部是一个 justify-content: space-between(logo 靠左、链接靠右)加 align-items: center 和链接间 gap 的容器。工具栏是一排 align-items: center 的子项。带图标和两行文字的卡片是 column 方向的 flex 容器,子项之间留 gap。要做”侧栏 + 内容”的两栏页面壳,可以让内容列 flex: 1,侧栏保持固定宽度,主区域吸收剩余空间。
一旦布局需要同时在二维对齐——整页骨架、行列都要对齐的卡片网格——flexbox 就不够用了。那是 CSS Grid 的职责:grid 一开始就同时定义行和列,flexbox 一次只排一根轴。常见的架构是:网格搭整页骨架,flexbox 在每个单元格内部做精细对齐,两者配合得天衣无缝。
速查
flex-direction决定主轴(row = 水平,column = 垂直);交叉轴与它垂直。justify-content作用在主轴,align-items作用在交叉轴。- 默认
align-items: stretch——子项填满交叉尺寸,除非另有指定。 flex-wrap: wrap+gap= 无需 margin 技巧的响应式行。flex-basis= 初始尺寸,flex-grow= 剩余空间份额,flex-shrink= 压缩速度。flex: 1=1 1 0%,flex: none=0 0 auto。- 子项默认会缩(
flex-shrink: 1),且拒绝缩到内容尺寸以下(min-width: auto)——被咬到就钉住其中一个。 - 一次一根轴用 flexbox,二维布局用 grid。
想可视化设计 flex 布局?CSS Flexbox Generator 让你拖拽对齐和尺寸选项并直接复制成品 CSS;CSS Grid Generator 对 grid 做同样的事。