Flex 布局
Flex 用于安排一行或一列中的元素,也能让多个子项在空间不足时换行。后台顶栏、侧栏与内容区、搜索工具条和统计卡片都可以用它完成。
本节先认识主轴与交叉轴,再练习对齐、间距、换行和子项伸缩,最后把这些规则用到学员管理后台。
创建 Flex 容器并认识坐标轴
创建容器与子项
.toolbar {
display: flex;
}.toolbar 成为 Flex 容器,它的直接子元素成为 Flex 子项。更深层的后代不会自动成为这一层的 Flex 子项。
<div class="toolbar">
<form class="search-form">...</form>
<a class="add-button" href="/students/new">新增学员</a>
</div>认识 Flex 术语
Flex 由容器和子项共同完成:display: flex 等规则写在容器上,伸缩规则写在直接子项上。先通过示意图认识两个方向。
提示
不要把主轴固定理解为横向。flex-direction 改变后,主轴和交叉轴的方向也会随之改变。
- main axis(主轴):Flex 子项依次排列的方向。
- main-start / main-end:主轴的起点和终点。
- cross axis(交叉轴):与主轴垂直的方向。
- cross-start / cross-end:交叉轴的起点和终点。
识别主轴与交叉轴
默认 flex-direction: row:
主轴:从左到右 →
交叉轴:从上到下 ↓改为纵向:
.sidebar-nav {
display: flex;
flex-direction: column;
}此时主轴从上到下,交叉轴变为水平方向。
理解轴比死记“水平居中”和“垂直居中”更可靠,因为方向改变后,对齐属性的作用方向也会改变。
设置主轴方向
使用 flex-direction 属性可以设置主轴,从而确定 flex 子元素的放置方向。flexbox 是单向布局概念,将 flexbox 中的内容在水平列或者垂直列进行布局
.container {
display: flex;
flex-direction: row;
}flex-direction 常用值包括 row、row-reverse、column 和 column-reverse。上面的代码只展示一个可以直接运行的写法,其他值放在文字说明中比较。
- row(默认值):从左到右是 ltr,从右到左是 rtl。
- row-reverse:从右到左是 ltr,从左到右是 rtl。
- column:与上面相同,但是 row 变为从上到下
- column-reverse:与相同 row-reverse,但从下到上
row(默认)
默认方向是行。这意味着主轴是水平的,而横轴是垂直的。这也意味着主轴从左侧开始,而横轴从顶部开始。
.flex-container {
display: flex;
flex-direction: row;
}row-reverse
与 row 类似,我们有 row-reverse。该方向不会交换主轴/交叉轴,但会交换主轴开始的位置。主轴现在从右边开始,而横轴没有改变,仍然从顶部开始。
.flex-container {
display: flex;
flex-direction: row-reverse;
}column
列方向完全交换了我们的轴,所以现在主轴是垂直的,横轴是水平的。
.flex-container {
display: flex;
flex-direction: column;
}column-reverse
column-reverse 方向与 column 基本相同,但它反转了主轴的方向,因此现在项目从容器的底部开始。
.flex-container {
display: flex;
flex-direction: column-reverse;
}.row {
display: flex;
flex-direction: row;
}
.column {
display: flex;
flex-direction: column;
}还存在 row-reverse 和 column-reverse,但它们会改变视觉顺序。页面阅读顺序和键盘导航仍主要受 HTML 源码顺序影响,基础项目优先保持源码顺序与视觉顺序一致。
对齐 Flex 子项
沿主轴对齐
这个属性定义了子元素沿主轴的对齐方式。当一行上所有的子元素都不没有占满整个空间时,有助于分配额外的剩余空间。如果子元素已经占满父元素的宽度并溢出到下一行,可以对子元素的对齐方式进行控制
.container {
display: flex;
justify-content: space-between;
}常用值:
| 值 | 作用 |
|---|---|
flex-start | 靠主轴起点 |
center | 主轴居中 |
flex-end | 靠主轴终点 |
space-between | 首尾贴边,中间均分剩余空间 |
space-around | 每项两侧分配空间 |
space-evenly | 每项元素距离相等 |
如果容器没有剩余空间,对齐差异可能看不出来。
对齐演示代码
对于下方所有示例,我们将假设 flex-items 的宽度为 20%。
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8"/>
<title>flex-direction</title>
<style>
.flex-container {
display: flex;
border: 1px dashed black;
justify-content: flex-start;
}
.flex-item {
width: 20%;
height: 10rem;
background-color: deepskyblue;
border: 1px solid black;
}
</style>
</head>
<body>
<div class="flex-container">
<div class="flex-item">1</div>
<div class="flex-item">2</div>
<div class="flex-item">3</div>
</div>
</body>
</html>1、flex-start(默认)
将所有项目放置在主轴的起点,默认情况下是轴的左侧。这也是 justify-content 的默认行为。
css.flex-container { display: flex; justify-content: flex-start; }
2、flex-end
将所有项目放置在主轴的末端,默认情况下是轴的右侧。
css.flex-container { display: flex; justify-content: flex-end; }
3、center
将所有项目放置在主轴的中心。这是在 CSS 中居中元素的最简单方法之一。
css.flex-container { display: flex; justify-content: center; }
4、space-between
这会占用容器内的所有额外空间,并将其均匀分布在每个元素之间,以在填充整个容器的同时将它们彼此间隔得尽可能远。
css.flex-container { display: flex; justify-content: space-between; }
5、space-around
这与 space-between 非常相似,但它也在容器外部和第一个/最后一个元素之间增加了空间。容器外部和第一个/最后一个元素之间的空间量正好是元素之间空间量的一半。
css.flex-container { display: flex; justify-content: space-around; }
总结
| 属性名 | 效果 |
|---|---|
| flex-start | 默认值从头部开始如果主轴是 x 轴,则从左到右 |
| flex-end | 从尾部开始排列 |
| center | 在主轴居中对齐 (如果主轴是 x 轴则水平居中) |
| space-around | 平分剩余空间 |
| space-between | 先两边贴边再平分剩余空间 (重要) |
沿交叉轴对齐
align-items 这定义了弹性项目沿当前行的交叉轴布局方式的默认行为。将其 justify-content 视为十字轴(垂直于主轴)的版本。
该属性是控制子项在侧轴(默认是 y 轴)上的排列方式 在子项为单项(单行)的时候使用
.container {
display: flex;
align-items: center;
}align-items 常用值包括 stretch、flex-start、flex-end、center 和 baseline。不同浏览器支持的扩展值不作为本节主线。
- stretch(默认值):拉伸以填充容器(仍遵循最小宽度/最大宽度)
- flex-start / start / self-start:项目放置在十字轴的开头。这些之间的区别是微妙的。
- flex-end / end / self-end:末端对齐
- center:在十字轴上居中
- baseline:项目对齐,例如其基线对齐
对于下方这些示例,Flex 子项宽度相同,但高度可以不同。
<style>
.flex-container {display: flex;border: 1px dashed black;}
.flex-container .item {width: 100px;background-color: deepskyblue;border: 1px solid;}
.flex-container .item:nth-child(1) {height: 60px;}
.flex-container .item:nth-child(2) {height: 80px;}
.flex-container .item:nth-child(3) {height: 100px;}
</style>
<div class="flex-container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>1、flex-start
这与 justify-content 的 flex-start 的工作原理相同,但默认情况下将从交叉轴的顶部开始。
css.flex-container { display: flex; align-items: flex-start; }
2、flex-end
这与 justify-content 的 flex-end 的工作原理相同,但默认情况下将从交叉轴的底部开始。
css.flex-container { display: flex; align-items: flex-end; }
3、center
这与 justify-content 的
center相同,但将基于交叉轴居中。css.flex-container { display: flex; align-items: center; }
4、stretch(默认)
css.flex-container { display: flex; align-items: stretch; }stretch只会拉伸交叉轴尺寸仍为auto的子项;明确设置height的子项不会被拉伸。
| 属性名 | 效果 |
|---|---|
| flex-start | 默认值从头部开始如果主轴是 x 轴,则从左到右 |
| flex-end | 从尾部开始排列 |
| center | 在主轴居中对齐 (如果主轴是 x 轴则水平居中) |
| stretch | 拉伸 |
控制间距与换行
使用 gap 设置间距
.stats-cards {
display: flex;
gap: 16px;
}gap 直接管理 Flex 子项之间的距离,不会在容器外侧额外增加间距。也可以分别设置行列:
.stats-cards {
row-gap: 20px;
column-gap: 16px;
}同一组元素优先使用 gap,比给每个子项加右外边距更容易处理换行和最后一项。
.container {
display: flex;
...
gap: 10px;
}使用 flex-wrap 换行
默认情况下,Flex 子项会尽量排在同一行。使用 flex-wrap 可以允许子项在空间不足时换行。
.container {
display: flex;
flex-wrap: wrap;
}flex-wrap 常用值包括 nowrap、wrap 和 wrap-reverse。上面的代码展示允许换行的常见写法。
- nowrap(默认值):所有子元素都将在一行上
- wrap:子元素将从上到下换行到多行。
- wrap-reverse:子元素将从下到上换行到多行。
子项伸缩
flex-grow
.main-content {
flex-grow: 1;
}有剩余空间时,flex-grow: 1 允许该子项扩展。
flex-shrink
.sidebar {
width: 240px;
flex-shrink: 0;
}空间不足时,默认子项可能缩小。固定侧栏常设置 flex-shrink: 0。
flex-basis
.stat-card {
flex-basis: 240px;
}flex-basis 指定沿主轴分配空间时的基础尺寸。
flex 简写
.main-content {
flex: 1;
}
.stat-card {
flex: 1 1 240px;
}flex: 1 1 240px 依次表示可增长、可收缩、基础尺寸为 240px。flex: 1 常用于让内容区占据剩余空间。
使用 Flex 排列统计卡片
统计卡片需要在宽屏并排显示,空间不足时自动换行。沿用前面学过的 flex-wrap、gap 和子项伸缩即可完成:
<section class="stats-cards">
<article class="stat-card">学员总数:128</article>
<article class="stat-card">本周新增:8</article>
<article class="stat-card">待审核:3</article>
<article class="stat-card">已停用:7</article>
</section>.stats-cards {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.stat-card {
flex: 1 1 220px;
min-width: 0;
padding: 20px;
border-radius: 12px;
background-color: #ffffff;
}flex-wrap: wrap允许卡片在空间不足时换到下一行。flex: 1 1 220px以 220px 为基础尺寸,并允许卡片伸缩。gap管理卡片之间的行距和列距。
窗口变窄时,卡片会依次换行;同一行中的卡片会分配剩余宽度。后面的综合案例继续使用这套写法。
完成后台主框架
<div class="app">
<header class="topbar">
<a class="brand" href="/">学员管理</a>
<p class="topbar-user">管理员:正心老师</p>
</header>
<div class="app-body">
<aside class="sidebar">
<nav class="sidebar-nav" aria-label="后台导航">
<a href="/dashboard">数据概览</a>
<a href="/students">学员管理</a>
<a href="/courses">课程管理</a>
</nav>
</aside>
<main class="main-content">
<div class="page-header">
<div>
<h1>学员管理</h1>
<p>查询和维护学员资料。</p>
</div>
<a href="/students/new">新增学员</a>
</div>
</main>
</div>
</div>.app {
min-height: 100vh;
}
.topbar {
display: flex;
min-height: 64px;
padding: 0 24px;
align-items: center;
gap: 24px;
background-color: #ffffff;
}
.topbar-user {
margin-left: auto;
}
.app-body {
display: flex;
min-height: calc(100vh - 64px);
}
.sidebar {
width: 240px;
flex-shrink: 0;
padding: 24px 16px;
background-color: #17233d;
}
.sidebar-nav {
display: flex;
flex-direction: column;
gap: 8px;
}
.main-content {
flex: 1;
min-width: 0;
padding: 24px;
background-color: #f4f7fb;
}
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 24px;
}下一节会在这套骨架上补齐统计卡片、搜索区、表格与窄屏处理。
小结
- Flex 只直接管理容器的子元素。
justify-content沿主轴对齐,align-items沿交叉轴对齐。gap管理子项间距,flex-wrap允许换行。- 固定侧栏常用
flex-shrink: 0,内容区常用flex: 1; min-width: 0。 - Flex 可以组合主框架、工具条和可换行的统计卡片,定位只处理局部覆盖。
Flex 主框架与统计卡片源码
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>后台主框架</title>
<style>
.app {
min-height: 100vh;
}
.topbar {
display: flex;
min-height: 64px;
padding: 0 24px;
align-items: center;
gap: 24px;
background-color: #ffffff;
}
.topbar-user {
margin-left: auto;
}
.app-body {
display: flex;
min-height: calc(100vh - 64px);
}
.sidebar {
width: 240px;
flex-shrink: 0;
padding: 24px 16px;
background-color: #17233d;
}
.sidebar-nav {
display: flex;
flex-direction: column;
gap: 8px;
}
.main-content {
flex: 1;
min-width: 0;
padding: 24px;
background-color: #f4f7fb;
}
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 24px;
}
</style>
</head>
<body>
<div class="app">
<header class="topbar">
<a class="brand" href="/">学员管理</a>
<p class="topbar-user">管理员:正心老师</p>
</header>
<div class="app-body">
<aside class="sidebar">
<nav class="sidebar-nav" aria-label="后台导航">
<a href="/dashboard">数据概览</a>
<a href="/students">学员管理</a>
<a href="/courses">课程管理</a>
</nav>
</aside>
<main class="main-content">
<div class="page-header">
<div>
<h1>学员管理</h1>
<p>查询和维护学员资料。</p>
</div>
<a href="/students/new">新增学员</a>
</div>
</main>
</div>
</div>
</body>
</html><!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flex 统计卡片</title>
<style>
body {
background-color: #f4f7fb;
}
.stats-cards {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.stat-card {
flex: 1 1 220px;
min-width: 0;
padding: 20px;
border-radius: 12px;
background-color: #ffffff;
}
</style>
</head>
<body>
<section class="stats-cards">
<article class="stat-card">学员总数:128</article>
<article class="stat-card">本周新增:8</article>
<article class="stat-card">待审核:3</article>
</section>
</body>
</html>