Skip to content

Flex 布局 ​

Flex 用于安排一行或一列中的元素,也能让多个子项在空间不足时换行。后台顶栏、侧栏与内容区、搜索工具条和统计卡片都可以用它完成。

本节先认识主轴与交叉轴,再练习对齐、间距、换行和子项伸缩,最后把这些规则用到学员管理后台。

创建 Flex 容器并认识坐标轴 ​

创建容器与子项 ​

css
.toolbar {
    display: flex;
}

.toolbar 成为 Flex 容器,它的直接子元素成为 Flex 子项。更深层的后代不会自动成为这一层的 Flex 子项。

html

<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:

text
主轴:从左到右  →
交叉轴:从上到下 ↓

改为纵向:

css
.sidebar-nav {
    display: flex;
    flex-direction: column;
}

此时主轴从上到下,交叉轴变为水平方向。

理解轴比死记“水平居中”和“垂直居中”更可靠,因为方向改变后,对齐属性的作用方向也会改变。

设置主轴方向 ​

使用 flex-direction 属性可以设置主轴,从而确定 flex 子元素的放置方向。flexbox 是单向布局概念,将 flexbox 中的内容在水平列或者垂直列进行布局

css
.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(默认)

默认方向是行。这意味着主轴是水平的,而横轴是垂直的。这也意味着主轴从左侧开始,而横轴从顶部开始。

css
.flex-container {
    display: flex;
    flex-direction: row;
}

row-reverse

与 row 类似,我们有 row-reverse。该方向不会交换主轴/交叉轴,但会交换主轴开始的位置。主轴现在从右边开始,而横轴没有改变,仍然从顶部开始。

css
.flex-container {
    display: flex;
    flex-direction: row-reverse;
}

column

列方向完全交换了我们的轴,所以现在主轴是垂直的,横轴是水平的。

css
.flex-container {
    display: flex;
    flex-direction: column;
}

column-reverse

column-reverse 方向与 column 基本相同,但它反转了主轴的方向,因此现在项目从容器的底部开始。

css
.flex-container {
    display: flex;
    flex-direction: column-reverse;
}

css
.row {
    display: flex;
    flex-direction: row;
}

.column {
    display: flex;
    flex-direction: column;
}

还存在 row-reverse 和 column-reverse,但它们会改变视觉顺序。页面阅读顺序和键盘导航仍主要受 HTML 源码顺序影响,基础项目优先保持源码顺序与视觉顺序一致。

对齐 Flex 子项 ​

沿主轴对齐 ​

这个属性定义了子元素沿主轴的对齐方式。当一行上所有的子元素都不没有占满整个空间时,有助于分配额外的剩余空间。如果子元素已经占满父元素的宽度并溢出到下一行,可以对子元素的对齐方式进行控制

css
.container {
  display: flex;
  justify-content: space-between;
}

常用值:

值作用
flex-start靠主轴起点
center主轴居中
flex-end靠主轴终点
space-between首尾贴边,中间均分剩余空间
space-around每项两侧分配空间
space-evenly每项元素距离相等

如果容器没有剩余空间,对齐差异可能看不出来。

对齐演示代码

对于下方所有示例,我们将假设 flex-items 的宽度为 20%。

html
<!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 轴)上的排列方式 在子项为单项(单行)的时候使用

css
.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 子项宽度相同,但高度可以不同。

html
<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 设置间距 ​

css
.stats-cards {
    display: flex;
    gap: 16px;
}

gap 直接管理 Flex 子项之间的距离,不会在容器外侧额外增加间距。也可以分别设置行列:

css
.stats-cards {
    row-gap: 20px;
    column-gap: 16px;
}

同一组元素优先使用 gap,比给每个子项加右外边距更容易处理换行和最后一项。

css
.container {
  display: flex;
  ...
  gap: 10px;
}

使用 flex-wrap 换行 ​

默认情况下,Flex 子项会尽量排在同一行。使用 flex-wrap 可以允许子项在空间不足时换行。

css
.container {
  display: flex;
  flex-wrap: wrap;
}

flex-wrap 常用值包括 nowrap、wrap 和 wrap-reverse。上面的代码展示允许换行的常见写法。

  • nowrap(默认值):所有子元素都将在一行上
nowrap · 保持单行,可横向查看
1
2
3
4
5
6
7
8
  • wrap:子元素将从上到下换行到多行。
  • wrap-reverse:子元素将从下到上换行到多行。

子项伸缩 ​

flex-grow ​

css
.main-content {
    flex-grow: 1;
}

有剩余空间时,flex-grow: 1 允许该子项扩展。

flex-shrink ​

css
.sidebar {
    width: 240px;
    flex-shrink: 0;
}

空间不足时,默认子项可能缩小。固定侧栏常设置 flex-shrink: 0。

flex-basis ​

css
.stat-card {
    flex-basis: 240px;
}

flex-basis 指定沿主轴分配空间时的基础尺寸。

flex 简写 ​

css
.main-content {
    flex: 1;
}

.stat-card {
    flex: 1 1 240px;
}

flex: 1 1 240px 依次表示可增长、可收缩、基础尺寸为 240px。flex: 1 常用于让内容区占据剩余空间。

使用 Flex 排列统计卡片 ​

统计卡片需要在宽屏并排显示,空间不足时自动换行。沿用前面学过的 flex-wrap、gap 和子项伸缩即可完成:

html
<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>
css
.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 管理卡片之间的行距和列距。

窗口变窄时,卡片会依次换行;同一行中的卡片会分配剩余宽度。后面的综合案例继续使用这套写法。

完成后台主框架 ​

html

<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>
css
.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;
}

下一节会在这套骨架上补齐统计卡片、搜索区、表格与窄屏处理。

小结 ​

  1. Flex 只直接管理容器的子元素。
  2. justify-content 沿主轴对齐,align-items 沿交叉轴对齐。
  3. gap 管理子项间距,flex-wrap 允许换行。
  4. 固定侧栏常用 flex-shrink: 0,内容区常用 flex: 1; min-width: 0。
  5. Flex 可以组合主框架、工具条和可换行的统计卡片,定位只处理局部覆盖。
Flex 主框架与统计卡片源码
html
<!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>
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>