CSS 布局体系:Flexbox 与 Grid——一维和二维的分工协作
前言
上一篇我们学了盒模型,知道了每个元素是一个矩形盒子。但问题来了:这些盒子怎么排列?水平并排?垂直居中?两栏布局?响应式网格?
CSS 布局方案经历了从 float → position → Flexbox → Grid 的演进。现代开发中,Flexbox 和 Grid 是绝对主力,float 基本退居二线。
两者的核心区别很简单:Flex 管一条线(一维),Grid 管一张表(二维)。这篇文章会带你掌握两者的核心用法,并在最后告诉你什么时候用哪个。
本文篇幅较长,建议边看边在浏览器中动手试。Chrome DevTools 内置了 Flex 和 Grid 的可视化调试工具,在 Elements 面板中选中 Flex/Grid 容器时会自动显示辅助线。
正文
1. display 属性:布局的起点
在学 Flex 和 Grid 之前,先理解 display 属性——它决定元素的布局行为:
display: block; /* 块级:独占一行(div, p, h1 的默认值) */
display: inline; /* 行内:与其他元素同行(span, a 的默认值) */
display: inline-block; /* 行内块:同行但可设宽高(img 的行为) */
display: none; /* 隐藏元素(不占空间) */
display: flex; /* ⭐ 弹性盒子布局 */
display: grid; /* ⭐ 网格布局 */
2. Flexbox 弹性盒子布局
Flexbox 是一维布局模型——在一条轴线上排列子元素(水平或垂直)。
2.1 核心概念
主轴方向(默认水平 →)
─────────────────────────────────►
┌──────────────────────────────────┐ ▲
│ ┌──────┐ ┌──────┐ ┌──────┐ │ │
│ │ item │ │ item │ │ item │ │ │ 交叉轴
│ │ 1 │ │ 2 │ │ 3 │ │ │ (垂直)
│ └──────┘ └──────┘ └──────┘ │ │
└──────────────────────────────────┘ ▼
flex container
- Flex 容器(container):设置
display: flex的父元素 - Flex 项目(item):容器的直接子元素
- 主轴(main axis):项目排列的方向(默认水平从左到右)
- 交叉轴(cross axis):垂直于主轴的方向
2.2 容器属性
.container {
display: flex;
/* 1. 主轴方向 */
flex-direction: row; /* 默认:水平从左到右 → */
flex-direction: row-reverse; /* 水平从右到左 ← */
flex-direction: column; /* 垂直从上到下 ↓ */
flex-direction: column-reverse; /* 垂直从下到上 ↑ */
/* 2. 是否换行 */
flex-wrap: nowrap; /* 默认:不换行(挤在一行) */
flex-wrap: wrap; /* 换行 */
/* 3. 主轴对齐(分配水平方向的空间) */
justify-content: flex-start; /* 靠左(默认) */
justify-content: flex-end; /* 靠右 */
justify-content: center; /* 居中 */
justify-content: space-between; /* 两端对齐,间距均分 */
justify-content: space-around; /* 每个元素两侧间距相等 */
justify-content: space-evenly; /* 所有间距完全相等 */
/* 4. 交叉轴对齐(分配垂直方向的空间) */
align-items: stretch; /* 默认:拉伸填满容器高度 */
align-items: flex-start; /* 顶部对齐 */
align-items: flex-end; /* 底部对齐 */
align-items: center; /* 垂直居中 */
align-items: baseline; /* 文字基线对齐 */
/* 5. 多行时交叉轴对齐 */
align-content: flex-start;
align-content: center;
align-content: space-between;
/* 6. 项目之间的间距(推荐用 gap 代替 margin) */
gap: 16px; /* 行列间距相同 */
gap: 16px 24px; /* 行间距 列间距 */
}
2.3 项目属性
.item {
/* 1. 放大比例(默认 0,不放大) */
flex-grow: 1; /* 等分剩余空间 */
/* 2. 缩小比例(默认 1,空间不足时等比缩小) */
flex-shrink: 0; /* 不缩小 */
/* 3. 基础尺寸 */
flex-basis: 200px; /* 类似 width,但更灵活 */
/* 简写(最常用) */
flex: 1; /* flex-grow:1, flex-shrink:1, flex-basis:0% */
flex: 0 0 300px; /* 不放大、不缩小、固定 300px */
/* 4. 单个项目的交叉轴对齐(覆盖容器的 align-items) */
align-self: center;
/* 5. 排序 */
order: -1; /* 数值越小排越前,默认 0 */
}
2.4 Flex 经典布局实战
💡 工程师手记:CSS 居中曾是前端面试的经典难题——垂直居中尤其折磨人,需要各种 hack。Flexbox 用两行代码终结了这个噩梦。这大概是我学前端时最有”时代进步”感触的一刻。
(建议替换为你自己学 Flexbox 时的真实感受)
水平垂直居中(最经典的用法):
.center-box {
display: flex;
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
height: 100vh;
}
导航栏:
.navbar {
display: flex;
justify-content: space-between; /* logo 在左,菜单在右 */
align-items: center;
padding: 0 24px;
height: 60px;
}
圣杯布局(header + 三栏 + footer):
.layout { display: flex; flex-direction: column; min-height: 100vh; }
.header { height: 60px; }
.main { display: flex; flex: 1; }
.sidebar { width: 250px; flex-shrink: 0; }
.content { flex: 1; }
.footer { height: 80px; }
<div class="layout">
<header class="header">头部</header>
<div class="main">
<aside class="sidebar">侧边栏</aside>
<main class="content">主内容</main>
</div>
<footer class="footer">底部</footer>
</div>
等宽卡片列表:
.card-list {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.card {
flex: 1 1 300px; /* 最小 300px,自动放大填满,换行 */
}
3. Grid 网格布局
Grid 是二维布局模型——同时控制行和列,适合复杂的页面布局。
3.1 核心概念
列1 列2 列3
┌─────────┬─────────┬─────────┐
│ cell │ cell │ cell │ 行1
│ (1,1) │ (1,2) │ (1,3) │
├─────────┼─────────┼─────────┤
│ cell │ cell │ cell │ 行2
│ (2,1) │ (2,2) │ (2,3) │
└─────────┴─────────┴─────────┘
线(line):分隔行/列的线,从 1 开始编号
轨道(track):两条线之间的空间(即一行或一列)
单元格(cell):行与列交叉形成的区域
区域(area):一个或多个单元格组成的矩形区域
3.2 容器属性
.grid-container {
display: grid;
/* 1. 定义列 */
grid-template-columns: 200px 1fr 200px; /* 三列:固定-弹性-固定 */
grid-template-columns: repeat(3, 1fr); /* 三列等宽 */
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); /* 响应式 */
/* 2. 定义行 */
grid-template-rows: 60px 1fr auto; /* 固定头-弹性中-自适应尾 */
/* 3. 间距 */
gap: 16px;
gap: 16px 24px; /* 行间距 列间距 */
/* 4. 区域命名布局(非常直观) */
grid-template-areas:
"header header header"
"sidebar main main"
"footer footer footer";
/* 5. 对齐(与 Flex 类似) */
justify-items: center; /* 单元格内水平对齐 */
align-items: center; /* 单元格内垂直对齐 */
justify-content: center; /* 整个网格在容器内水平对齐 */
align-content: center; /* 整个网格在容器内垂直对齐 */
}
3.3 fr 单位
fr(fraction)是 Grid 专用的弹性单位,表示”剩余空间的份数”:
grid-template-columns: 1fr 2fr 1fr;
/* 三列按 1:2:1 分配空间 */
/* 如果容器 800px,间距 0:200px + 400px + 200px */
grid-template-columns: 200px 1fr;
/* 左列固定 200px,右列占满剩余空间 */
类比:
fr类似 Flex 的flex-grow——按比例分配剩余空间。
3.4 项目属性
.grid-item {
/* 通过网格线定位 */
grid-column: 1 / 3; /* 从第1条列线到第3条列线(跨2列) */
grid-row: 1 / 2; /* 第1行 */
/* 简写 */
grid-column: span 2; /* 跨2列 */
grid-row: span 3; /* 跨3行 */
/* 使用区域名(配合 grid-template-areas) */
grid-area: header;
}
3.5 Grid 经典布局实战
经典页面布局(区域命名法):
.page {
display: grid;
grid-template-columns: 250px 1fr;
grid-template-rows: 60px 1fr 80px;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
min-height: 100vh;
gap: 0;
}
.header { grid-area: header; background: #1a1a2e; color: white; }
.sidebar { grid-area: sidebar; background: #f0f0f0; }
.main { grid-area: main; padding: 24px; }
.footer { grid-area: footer; background: #333; color: white; }
响应式卡片网格:
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 24px;
padding: 24px;
}
auto-fill+minmax()是响应式布局的利器:卡片最小 280px,自动填充列数,剩余空间平分。
💡 工程师手记:第一次看到
grid-template-areas用字符串”画”布局图时,我的反应是:这也太直观了吧?HTML 定义结构,CSS 里直接”画”出布局——这比 FPGA 的约束文件直观多了。(建议替换为你自己第一次使用 Grid areas 的感受)
12 列栅格系统:
.grid-12 {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 24px;
}
.col-6 { grid-column: span 6; } /* 占半行 */
.col-4 { grid-column: span 4; } /* 占 1/3 */
.col-3 { grid-column: span 3; } /* 占 1/4 */
.col-12 { grid-column: span 12; } /* 占满一行 */
4. ★ Flexbox vs Grid:什么时候用哪个?
这是本文的核心问题。先看一个简单的判断流程:
你要控制的是一行/一列中的排列? → Flex
你要同时控制行和列的对齐? → Grid
不确定? → 先用 Flex,发现不够用再换 Grid
具体场景对照:
| 场景 | 推荐 | 原因 |
|---|---|---|
| 一行内多个元素排列 | Flex | 一维布局更简单 |
| 导航栏、按钮组 | Flex | 线性排列 |
| 水平垂直居中 | Flex | 两行代码搞定 |
| 复杂页面整体布局 | Grid | 二维控制更精确 |
| 卡片网格、响应式列表 | Grid | auto-fill + minmax 很强大 |
| 表单对齐 | Grid | 标签和输入框完美对齐 |
经验法则:组件内部用 Flex,页面布局用 Grid。两者经常搭配使用,不是二选一。比如:页面整体用 Grid 定义 header/sidebar/main/footer 的位置,而 navbar 内部的菜单项用 Flex 排列。
💡 工程师手记:我的经验是:不确定用哪个时先用 Flex。Flex 更简单、更直觉,90% 的场景都能应付。只有当你发现自己在用 Flex 嵌套 Flex 来模拟网格布局时,才需要换成 Grid。
(建议替换为你自己的布局选型经验)
5. position 定位(补充)
虽然 Flex/Grid 解决了大部分布局需求,但 position 在特定场景仍然必不可少:
position: static; /* 默认,按文档流排列 */
position: relative; /* 相对定位:相对于自身原位置偏移,不脱离文档流 */
position: absolute; /* 绝对定位:相对于最近的 relative/absolute 祖先定位 */
position: fixed; /* 固定定位:相对于浏览器窗口,滚动不动(如固定导航栏) */
position: sticky; /* 粘性定位:滚动到指定位置后"粘住" */
常用组合——父 relative 子 absolute:
.card {
position: relative;
}
.badge {
position: absolute;
top: -8px;
right: -8px;
/* 标记定位在卡片右上角 */
}
固定导航栏:
.navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100; /* 确保在其他元素上面 */
}
常见问题
💬 你可能会问:justify-content 和 align-items 怎么记?总是混淆。
简单记法:
justify管主轴方向(默认水平),align管交叉轴方向(默认垂直)。如果你用flex-direction: column把主轴变成垂直,它们的作用方向也跟着交换。
💬 你可能会问:float 布局还需要学吗?
现在基本不需要了。float 最初是为了实现”图片环绕文字”的效果,后来被拿来做布局是因为没有更好的工具。现在有了 Flex 和 Grid,float 可以安心退休了。
💬 你可能会问:Grid 的浏览器兼容性怎么样?
2026 年,所有现代浏览器(Chrome、Firefox、Safari、Edge)都完全支持 Flex 和 Grid。除非你要兼容 IE 11(已经停止支持),否则放心用。
总结
| 知识点 | 核心要点 |
|---|---|
| display | flex(一维布局)和 grid(二维布局)是现代主力 |
| Flexbox | 容器:justify-content 控制主轴,align-items 控制交叉轴 |
| Flex 项目 | flex: 1 等分空间,flex: 0 0 Xpx 固定尺寸 |
| Grid | grid-template-columns/rows 定义网格,fr 分配弹性空间 |
| Grid 区域 | grid-template-areas 用名称直观定义布局 |
| 响应式网格 | repeat(auto-fill, minmax(Xpx, 1fr)) 是万能公式 |
| 选型策略 | 组件内部 Flex,页面布局 Grid,可混合使用 |
| position | fixed 固定定位、sticky 粘性定位、父 relative 子 absolute |
下一步行动:强烈推荐玩一遍 Flexbox Froggy 和 Grid Garden——用游戏的方式练习布局属性,比读十遍文档都有效。
参考资料
- MDN - Flexbox 指南
- MDN - Grid 指南
- A Complete Guide to Flexbox - CSS-Tricks
- A Complete Guide to Grid - CSS-Tricks
- Flexbox Froggy(游戏学 Flex)
- Grid Garden(游戏学 Grid)
📖 系列导航:本文是「FPGA 工程师的前端学习笔记」系列的第 4 篇 上一篇:CSS 入门:选择器与盒模型 下一篇:CSS 响应式设计与媒体查询