CSS Grid 与 Flexbox 布局实战完全指南
深入对比 Grid 和 Flexbox 两种布局方案,掌握响应式设计的核心技巧
在现代 Web 开发中,CSS Grid 和 Flexbox 是两大核心布局系统。它们各自擅长不同的场景,理解它们的差异与协作方式,是构建健壮响应式页面的基础。本文将从原理对比出发,逐步覆盖常见布局模式与响应式技巧。
一、Grid 与 Flexbox 的核心区别
Flexbox 是一维布局系统,旨在沿一条轴(水平或垂直)排列元素;Grid 是二维布局系统,可以同时控制行和列。这并不是说谁更”高级”,而是它们解决的问题维度不同。
| 对比维度 | Flexbox | Grid |
|---|---|---|
| 布局维度 | 一维(行或列) | 二维(行和列同时) |
| 设计思路 | 内容优先,从内容推算尺寸 | 布局优先,先定义网格再放内容 |
| 适合场景 | 导航栏、工具栏、卡片行 | 整体页面布局、复杂仪表盘 |
| 对齐控制 | 主轴/交叉轴对齐 | 行轨/列轨对齐,更精细 |
| 间距处理 | gap 属性 | gap / row-gap / column-gap |
| 浏览器支持 | 极佳 | 极佳(IE 11 部分支持) |
选择原则
当你需要沿一个方向排列一组元素时,用 Flexbox;当你需要同时控制行列结构时,用 Grid。
一个常见的实践是:用 Grid 搭建页面骨架(header、sidebar、main、footer),用 Flexbox 处理组件内部的一维排列。
二、Flexbox 布局精要
2.1 基本模型
.flex-container {
display: flex;
flex-direction: row; /* row | row-reverse | column | column-reverse */
justify-content: space-between; /* 主轴对齐 */
align-items: center; /* 交叉轴对齐 */
flex-wrap: wrap; /* 允许换行 */
gap: 16px;
}
.flex-item {
flex: 1 1 200px; /* flex-grow | flex-shrink | flex-basis */
}
flex 简写属性是 Flexbox 的灵魂。flex-grow 控制剩余空间分配比例,flex-shrink 控制空间不足时的收缩比例,flex-basis 定义初始尺寸。
2.2 经典导航栏
<nav class="navbar">
<div class="logo">Brand</div>
<ul class="nav-links">
<li><a href="#">首页</a></li>
<li><a href="#">产品</a></li>
<li><a href="#">关于</a></li>
</ul>
<button class="cta">登录</button>
</nav>
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 24px;
height: 64px;
}
.nav-links {
display: flex;
gap: 24px;
list-style: none;
margin: 0;
padding: 0;
}
2.3 flex-grow 与空间分配
/* 三栏:侧边栏固定,中间区域自适应 */
.sidebar { flex: 0 0 240px; }
.main { flex: 1 1 0; } /* 占据所有剩余空间 */
.aside { flex: 0 0 180px; }
三、Grid 布局精要
3.1 定义网格
.grid-container {
display: grid;
grid-template-columns: 200px 1fr 1fr; /* 三列 */
grid-template-rows: auto 1fr auto; /* 三行 */
gap: 20px;
}
fr 单位表示按比例分配剩余空间,类似于 Flexbox 的 flex-grow。
3.2 grid-template-areas 语义布局
这是 Grid 最直观的特性,可以用名字描述布局结构:
.page-layout {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-rows: 64px 1fr 48px;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
min-height: 100vh;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }
3.3 repeat 与 minmax
/* 自适应卡片网格:每列最小 280px,最大占满剩余空间 */
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 24px;
}
auto-fill 会自动生成尽可能多的列;auto-fit 则会将空列折叠,让现有元素拉伸填满。当卡片数量不足以占满一行时,auto-fit 会让卡片拉伸,而 auto-fill 会保留空位。
四、经典布局模式实现
4.1 圣杯布局
圣杯布局包含 header、footer、以及中间三栏(左定宽、右定宽、中间自适应)。用 Grid 实现非常简洁:
.holy-grail {
display: grid;
grid-template-columns: 200px 1fr 200px;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header header"
"nav main aside"
"footer footer footer";
min-height: 100vh;
}
.header { grid-area: header; }
.nav { grid-area: nav; }
.main { grid-area: main; }
.aside { grid-area: aside; }
.footer { grid-area: footer; }
4.2 双飞翼布局
双飞翼布局与圣杯布局的目标相同,但通过在内容区嵌套一个内层容器来避免定位问题。用现代 CSS 实现:
.double-wing {
display: grid;
grid-template-columns: 200px 1fr 200px;
}
.double-wing .center {
grid-column: 1 / -1; /* 跨越所有列 */
order: 2;
}
.double-wing .left { order: 1; }
.double-wing .right { order: 3; }
4.3 瀑布流布局
纯 CSS 实现瀑布流,Grid 的 masonry 值是未来方向(目前 Chrome 已开始支持),当下可用 column-count 方案:
/* 方案一:CSS Multi-column(当下可用) */
.masonry {
column-count: 3;
column-gap: 20px;
}
.masonry .item {
break-inside: avoid; /* 防止内容跨列断裂 */
margin-bottom: 20px;
}
/* 方案二:Grid Masonry(实验性) */
.masonry-future {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: masonry; /* 未来标准 */
gap: 20px;
}
五、响应式断点设计
5.1 常用断点
| 断点名称 | 宽度范围 | 典型设备 |
|---|---|---|
| xs | < 640px | 手机竖屏 |
| sm | 640px - 768px | 手机横屏 |
| md | 768px - 1024px | 平板 |
| lg | 1024px - 1280px | 小屏笔记本 |
| xl | > 1280px | 桌面显示器 |
5.2 移动优先的断点写法
/* 默认移动端:单列 */
.responsive-grid {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
}
/* 平板:两列 */
@media (min-width: 768px) {
.responsive-grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* 桌面:三列 */
@media (min-width: 1024px) {
.responsive-grid {
grid-template-columns: repeat(3, 1fr);
gap: 24px;
}
}
5.3 响应式导航栏切换
/* 移动端:垂直堆叠 */
.navbar {
display: flex;
flex-direction: column;
align-items: stretch;
}
/* 桌面端:水平排列 */
@media (min-width: 768px) {
.navbar {
flex-direction: row;
justify-content: space-between;
align-items: center;
}
}
六、容器查询:响应式的新范式
媒体查询基于视口宽度,而容器查询基于父容器宽度,使得组件级响应式成为可能。
6.1 基本用法
/* 1. 定义容器 */
.card-container {
container-type: inline-size;
/* 或使用简写 */
container: sidebar / inline-size;
}
/* 2. 根据容器宽度响应 */
@container (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 120px 1fr;
gap: 16px;
}
}
@container (max-width: 399px) {
.card {
display: flex;
flex-direction: column;
}
}
6.2 容器查询的优势
同一个卡片组件可以被放在侧边栏(窄)或主内容区(宽),它会根据自身容器尺寸自动调整布局,而无需关心页面整体的视口大小。这让真正可复用的组件系统成为现实。
<!-- 同一个组件,不同容器中表现不同 -->
<aside class="sidebar">
<article class="card">...</article> <!-- 窄容器,垂直布局 -->
</aside>
<main class="content">
<article class="card">...</article> <!-- 宽容器,水平布局 -->
</main>
七、Grid 与 Flexbox 的协作
实际项目中,两者经常搭配使用。典型模式:
/* Grid 做整体骨架 */
.dashboard {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-rows: 56px 1fr;
grid-template-areas:
"sidebar header"
"sidebar main";
height: 100vh;
}
/* Flexbox 做组件内部布局 */
.header {
grid-area: header;
display: flex;
align-items: center;
gap: 16px;
padding: 0 20px;
}
.main {
grid-area: main;
display: flex;
flex-wrap: wrap;
gap: 20px;
padding: 20px;
align-content: flex-start;
}
总结
| 场景 | 推荐方案 |
|---|---|
| 页面整体骨架 | Grid |
| 导航栏、工具栏 | Flexbox |
| 卡片网格 | Grid + auto-fit |
| 居中单个元素 | Flexbox |
| 复杂仪表盘 | Grid |
| 组件内部排列 | Flexbox |
| 响应式组件 | 容器查询 |
掌握 Grid 和 Flexbox 的关键不在于背诵属性,而在于理解它们的设计哲学:Grid 是从外向内——先定义网格结构,再放入内容;Flexbox 是从内向外——从内容出发,让容器适应。两者结合,再加上容器查询,几乎可以应对所有现代布局需求。