Appearance
06|CSS Flex 布局
传统的布局方式(float、position、inline-block)在处理对齐、分布、自适应时都很麻烦。Flexbox(弹性盒子)是 CSS 专门为一维布局设计的方案——元素在一行或一列中排列,需要控制它们的对齐方式、间距分配、伸缩比例时,Flex 是最直接的工具。
一、启用 Flex
给容器设置 display: flex,它的直接子元素就成为 flex 项:
html
<style>
.container {
display: flex;
}
.item {
width: 100px;
height: 60px;
background: #e6f7ff;
border: 1px solid #1890ff;
}
</style>
<div class="container">
<div class="item">A</div>
<div class="item">B</div>
<div class="item">C</div>
</div>三个 div 默认会水平排列成一行。在没有设置 display: flex 时,它们会垂直堆叠(因为 div 是块级元素)。
二、主轴方向
Flex 有两根轴:主轴(main axis)和交叉轴(cross axis)。默认主轴是水平的,从左到右。
css
.container {
display: flex;
flex-direction: row; /* 默认值:水平,左到右 */
}| 值 | 方向 |
|---|---|
row | 水平,左到右(默认) |
row-reverse | 水平,右到左 |
column | 垂直,上到下 |
column-reverse | 垂直,下到上 |
改成垂直排列:
css
.container {
display: flex;
flex-direction: column;
}三、主轴对齐(justify-content)
控制 flex 项在主轴上的分布:
css
.container {
display: flex;
justify-content: flex-start; /* 默认值:靠左 */
}| 值 | 效果 |
|---|---|
flex-start | 靠主轴起点 |
flex-end | 靠主轴终点 |
center | 居中 |
space-between | 两端对齐,项之间等距 |
space-around | 项两侧等距(两端间距是中间的一半) |
space-evenly | 所有间距完全相等 |
css
/* 导航栏:两端对齐,菜单项均匀分布 */
.nav {
display: flex;
justify-content: space-between;
}
/* 按钮组:居中排列 */
.button-group {
display: flex;
justify-content: center;
gap: 12px;
}四、交叉轴对齐(align-items)
控制 flex 项在交叉轴上的对齐方式。默认主轴水平,交叉轴就是垂直方向:
css
.container {
display: flex;
align-items: stretch; /* 默认值:拉伸填满容器高度 */
}| 值 | 效果 |
|---|---|
stretch | 拉伸至容器交叉轴长度(默认值) |
flex-start | 靠交叉轴起点(顶部) |
flex-end | 靠交叉轴终点(底部) |
center | 交叉轴居中 |
baseline | 文字基线对齐 |
css
/* 卡片内容垂直居中 */
.card {
display: flex;
align-items: center;
height: 120px;
}
/* 顶部对齐的多行卡片 */
.card-list {
display: flex;
align-items: flex-start;
}五、多行排列与行对齐
默认情况下,flex 项会挤在一行,超出容器宽度时可能溢出。允许换行:
css
.container {
display: flex;
flex-wrap: wrap;
}换行后,用 align-content 控制行与行之间的分布:
css
.container {
display: flex;
flex-wrap: wrap;
align-content: flex-start; /* 行靠顶部 */
gap: 16px; /* 项之间的间距 */
}gap 是较新的属性,同时设置行间距和列间距。也可以分别设置 row-gap 和 column-gap。
六、flex 项的伸缩
flex-grow
当容器主轴方向有剩余空间时,flex-grow 决定各 flex 项如何分配这些空间:
css
.container {
display: flex;
width: 500px;
}
.sidebar {
width: 100px;
flex-grow: 0; /* 不伸展,保持 100px */
}
.main {
flex-grow: 1; /* 占据所有剩余空间 */
}总剩余空间 = 500 - 100 = 400px。sidebar 的 flex-grow 是 0,不参与分配;main 的 flex-grow 是 1,独占 400px,最终宽度 400px。
css
/* 三栏布局:左右固定,中间自适应 */
.container {
display: flex;
}
.left { width: 200px; flex-grow: 0; }
.center { flex-grow: 1; }
.right { width: 200px; flex-grow: 0; }如果多个项都有 flex-grow > 0,剩余空间按 grow 值的比例分配:
css
.a { flex-grow: 1; } /* 占 1/(1+2) = 1/3 */
.b { flex-grow: 2; } /* 占 2/(1+2) = 2/3 */flex-shrink
当容器空间不足时,flex-shrink 决定各 flex 项如何收缩:
css
.item {
flex-shrink: 1; /* 默认值:允许收缩 */
}
.fixed {
flex-shrink: 0; /* 不允许收缩 */
}flex-basis
定义 flex 项在分配空间之前的初始大小:
css
.item {
flex-basis: 200px; /* 初始占 200px,再按 grow/shrink 调整 */
}flex-basis 与 width 的区别:width 是最终大小(受 box-sizing 影响),flex-basis 是分配空间前的基准值。在 flex 布局中,flex-basis 优先级高于 width。
简写
css
.item {
flex: 1; /* flex-grow: 1, flex-shrink: 1, flex-basis: 0% */
flex: 0 0 200px; /* 不伸展、不收缩、固定 200px */
flex: 1 1 auto; /* 可伸可缩,基准为自身内容大小 */
}七、单独对齐某个 flex 项
css
.container {
display: flex;
align-items: center;
}
.special {
align-self: flex-end; /* 这个项单独靠底部 */
}align-self 覆盖容器的 align-items 设置,只影响当前项。
八、常见场景
水平垂直居中
css
.center-box {
display: flex;
justify-content: center; /* 主轴居中 */
align-items: center; /* 交叉轴居中 */
height: 300px;
}这是实现单元素水平垂直居中最简单的方式,不需要计算 margin。
固定底部栏
css
.page {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.content {
flex-grow: 1; /* 内容区占满剩余空间 */
}
.footer {
flex-shrink: 0; /* 底部栏不收缩 */
}内容少时底部栏贴在视口底部,内容多时自然被撑下去。
等宽网格
css
.grid {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.grid-item {
flex: 0 0 calc(33.333% - 11px); /* 三列,扣除间距 */
}九、常见错误
在 flex 项上用 margin: auto 实现居中
css
/* 可以工作,但不直观 */
.item {
margin: auto;
}
/* 更推荐的方式 */
.container {
display: flex;
justify-content: center;
align-items: center;
}忘记设置 flex-wrap 导致溢出
css
/* 错误:项太多时会撑破容器 */
.container {
display: flex;
}
/* 正确:允许换行 */
.container {
display: flex;
flex-wrap: wrap;
}flex-grow 与 width 混用导致困惑
css
/* 同时设置时,flex-basis(默认 auto)会优先于 width */
.item {
width: 100px;
flex-grow: 1; /* 最终宽度不是 100px,而是 100px + 分配的剩余空间 */
}如果要固定宽度,用 flex: 0 0 100px,而不是 width: 100px。