Skip to content

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-gapcolumn-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。sidebarflex-grow 是 0,不参与分配;mainflex-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-basiswidth 的区别: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