写布局时最常见的纠结是:这块该用 Flexbox 还是 Grid?
很多人凭手感选,结果要么用 Flex 硬凑二维网格写得很别扭,要么用 Grid 排一行按钮显得小题大做。其实它们的分工有一条很清晰的界线。
一句话区分
- Flexbox 是一维布局:一次只管一个方向(一行,或者一列)
- Grid 是二维布局:同时管行和列
判断方法:如果你脑子里想的是"把这些东西排成一排/一列",用 Flex;如果你想的是"我要一个几行几列的结构",用 Grid。
Flexbox 的核心模型
Flexbox 围绕**主轴(main axis)和交叉轴(cross axis)**展开。
.container {
display: flex;
flex-direction: row; /* 主轴方向:row(默认横向)| column(纵向) */
justify-content: center; /* 主轴上如何对齐 */
align-items: center; /* 交叉轴上如何对齐 */
gap: 12px; /* 子项之间的间距 */
}记住两个对齐属性的分工:
justify-content管主轴(主轴是 row 时就是水平方向)align-items管交叉轴(与主轴垂直的方向)
这也是为什么"水平垂直居中"一行就能搞定:
.center {
display: flex;
justify-content: center;
align-items: center;
}flex 子项的伸缩
Flex 真正强大的地方是子项能按比例伸缩,核心是 flex 这个简写:
.item {
flex: 1; /* = flex-grow:1 flex-shrink:1 flex-basis:0 */
}flex 由三部分组成:
flex-grow:剩余空间如何放大分配(0 表示不放大)flex-shrink:空间不够时如何缩小(0 表示不缩小)flex-basis:分配前的基准尺寸
常见用法:
/* 三栏等宽 */
.col { flex: 1; }
/* 侧栏固定 240px,主区占满剩余 */
.sidebar { flex: 0 0 240px; }
.main { flex: 1; }flex: 0 0 240px 的意思是"不放大、不缩小、基准 240px",也就是一个固定宽度但又参与 Flex 布局的盒子。
Grid 的核心模型
Grid 让你直接声明一个网格结构。
.container {
display: grid;
grid-template-columns: 200px 1fr 1fr; /* 三列:固定 + 两个等分 */
grid-template-rows: auto 1fr auto; /* 三行 */
gap: 16px;
}最关键的新单位是 fr(fraction,剩余空间的份数):
grid-template-columns: 1fr 2fr; /* 第二列宽度是第一列的两倍 */repeat 和 minmax
列很多时用 repeat 简写:
grid-template-columns: repeat(3, 1fr); /* 等价于 1fr 1fr 1fr */minmax(最小值, 最大值) 限定尺寸范围,常用于响应式:
grid-template-columns: repeat(3, minmax(0, 1fr));这里的 minmax(0, 1fr) 是个实用技巧:它能防止内容(比如一段长文本或图片)把列撑爆,保证三列真正等宽。
自适应列数:auto-fill / auto-fit
不写死列数,让网格根据容器宽度自动决定放几列:
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 16px;
}这一行的含义是:每列至少 220px,容器能放下几列就放几列,放不下自动换行。一个纯 CSS、不写媒体查询的响应式卡片墙就完成了。
auto-fit 和 auto-fill 很像,区别在于列不满时:auto-fit 会让现有列拉伸填满整行,auto-fill 会保留空列。想让卡片铺满一行通常用 auto-fit。
用区域命名做整页布局
Grid 还能用命名区域,非常适合整页骨架:
.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; }布局结构在 CSS 里一目了然,比一堆嵌套 div 直观得多。
怎么选:一维用 Flex,二维用 Grid
回到最初的问题,给几个具体判断:
用 Flexbox 的场景:
- 一行导航/按钮组
- 一个组件内部的横向排布(图标 + 文字)
- 需要子项按比例伸缩(侧栏固定 + 主区自适应)
- 垂直居中一个元素
用 Grid 的场景:
- 整页布局骨架(头部/侧栏/主体/底部)
- 卡片墙、图片画廊
- 表格状的二维结构
- 需要精确控制行列对齐的复杂布局
一个经验法则:只关心"一排怎么排"用 Flex,关心"行和列同时怎么对齐"用 Grid。
它们不是二选一,而是配合
真实页面里,最常见的是 Grid 管大骨架,Flex 管局部。
比如一个典型的后台页:用 Grid 划分整体区域,每个区域内部用 Flex 排列具体内容。
/* 外层:Grid 划分页面 */
.page {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-rows: 64px 1fr;
grid-template-areas:
"sidebar header"
"sidebar main";
height: 100vh;
}
/* 顶栏内部:Flex 排 logo、搜索框、头像 */
.header {
grid-area: header;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
}
/* 卡片区:Grid 自适应铺卡片 */
.main {
grid-area: main;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 16px;
padding: 16px;
overflow: auto;
}
/* 单张卡片内部:Flex 纵向排内容 */
.card {
display: flex;
flex-direction: column;
gap: 8px;
}这套"Grid 搭骨架、Flex 填血肉"的组合,几乎能覆盖绝大多数页面。
几个实用细节
1. gap 两者都能用
以前 Flex 要靠 margin 凑间距,很麻烦。现在 gap 在 Flex 和 Grid 里都支持,优先用它。
2. 别忘了 min-width: 0
Flex 子项默认 min-width: auto,内容过长时不会缩小,会撑破布局。需要省略号或换行时,常常要给子项加 min-width: 0:
.flex-item {
flex: 1;
min-width: 0; /* 允许收缩,配合下面的省略号 */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}Grid 里对应的是前面提过的 minmax(0, 1fr),同样是为了允许收缩。
3. place-items 简写居中
Grid 里垂直水平居中可以一行:
.center {
display: grid;
place-items: center;
}常见坑
第一,用 Flex 硬排二维网格。子项换行后上下两行对不齐,得靠固定宽度凑。这种情况就该用 Grid。
第二,Grid 列被内容撑爆。忘了 minmax(0, 1fr),一张大图或一段不换行的长文本就把"等宽"破坏了。
第三,Flex 子项不收缩导致溢出。没加 min-width: 0,长文本撑破容器还不出省略号。
第四,过度嵌套。能用一层 Grid 区域解决的,别套五六层 div。命名区域往往比嵌套更清晰。
总结
- Flexbox 管一维:一行或一列的排布、对齐、按比例伸缩
- Grid 管二维:行列同时控制,适合整页骨架和卡片墙
- 选择标准:只排一排用 Flex,要行列结构用 Grid
- 实战里两者配合最常见:Grid 搭大骨架,Flex 填内部细节
- 记牢两个救命技巧:Flex 子项加
min-width: 0、Grid 列用minmax(0, 1fr),能解决大半溢出问题
把这两套模型吃透,几乎所有页面布局都能不靠 float、不靠绝对定位,干净利落地写出来。