如果说现代 CSS 布局最重要的两套工具是什么,答案基本就是:
flexgrid很多人知道:
flex很常用grid很强大但真正写项目时,还是会遇到很多问题:
- 为什么
flex: 1有时候不生效?- 为什么内容一长就把布局撑爆了?
- 为什么
grid明明写了三列,结果列宽不对?- 为什么
gap有时很好用,有时看起来又像没效果?- 为什么子元素加了
overflow、ellipsis、height: 100%结果怪怪的?这篇文章不只讲语法,而是把 Flex / Grid 的核心思路、详细使用、常见问题、注意事项 一次讲透。
目录
- [一、先说结论:什么时候用 Flex,什么时候用 Grid](#一、先说结论:什么时候用 Flex,什么时候用 Grid "#%E4%B8%80%E5%85%88%E8%AF%B4%E7%BB%93%E8%AE%BA%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99%E7%94%A8-flex%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99%E7%94%A8-grid")
- [二、Flex 到底是什么](#二、Flex 到底是什么 "#%E4%BA%8Cflex-%E5%88%B0%E5%BA%95%E6%98%AF%E4%BB%80%E4%B9%88")
- [三、Flex 的核心概念](#三、Flex 的核心概念 "#%E4%B8%89flex-%E7%9A%84%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5")
- [四、Flex 常用属性详解](#四、Flex 常用属性详解 "#%E5%9B%9Bflex-%E5%B8%B8%E7%94%A8%E5%B1%9E%E6%80%A7%E8%AF%A6%E8%A7%A3")
- [五、Flex 最常见的实战场景](#五、Flex 最常见的实战场景 "#%E4%BA%94flex-%E6%9C%80%E5%B8%B8%E8%A7%81%E7%9A%84%E5%AE%9E%E6%88%98%E5%9C%BA%E6%99%AF")
- [六、Flex 常见问题与注意事项](#六、Flex 常见问题与注意事项 "#%E5%85%ADflex-%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E4%B8%8E%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9")
- [七、Grid 到底是什么](#七、Grid 到底是什么 "#%E4%B8%83grid-%E5%88%B0%E5%BA%95%E6%98%AF%E4%BB%80%E4%B9%88")
- [八、Grid 的核心概念](#八、Grid 的核心概念 "#%E5%85%ABgrid-%E7%9A%84%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5")
- [九、Grid 常用属性详解](#九、Grid 常用属性详解 "#%E4%B9%9Dgrid-%E5%B8%B8%E7%94%A8%E5%B1%9E%E6%80%A7%E8%AF%A6%E8%A7%A3")
- [十、Grid 最常见的实战场景](#十、Grid 最常见的实战场景 "#%E5%8D%81grid-%E6%9C%80%E5%B8%B8%E8%A7%81%E7%9A%84%E5%AE%9E%E6%88%98%E5%9C%BA%E6%99%AF")
- [十一、Grid 常见问题与注意事项](#十一、Grid 常见问题与注意事项 "#%E5%8D%81%E4%B8%80grid-%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E4%B8%8E%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9")
- [十二、Flex 和 Grid 怎么配合使用](#十二、Flex 和 Grid 怎么配合使用 "#%E5%8D%81%E4%BA%8Cflex-%E5%92%8C-grid-%E6%80%8E%E4%B9%88%E9%85%8D%E5%90%88%E4%BD%BF%E7%94%A8")
- 十三、一张表总结核心区别
- 十四、结论
一、先说结论:什么时候用 Flex,什么时候用 Grid
先记住最实用的一句话:
- 一维布局,用 Flex
- 二维布局,用 Grid
什么叫一维布局?
就是你主要只关心一个方向:
- 横着一排怎么排
- 竖着一列怎么排
典型场景:
- 导航栏
- 按钮组
- 工具栏
- 表单一行
- 垂直菜单
- 居中对齐
这类优先用 flex。
什么叫二维布局?
就是你同时要关心:
- 行
- 列
典型场景:
- 卡片墙
- 图片网格
- 后台仪表盘
- 页面骨架布局
- 多行多列区域划分
这类优先用 grid。
一句更贴近项目的话:
- 局部小布局、对齐、伸缩,优先 Flex
- 整体网格、面板、区域分布,优先 Grid
二、Flex 到底是什么
flex 是一种 弹性盒布局(Flexible Box Layout)。
它的核心思想是:
让父元素控制子元素如何沿一个方向排列、对齐、伸缩。
也就是说:
flex主要是给父元素用的- 父元素一旦开启
display: flex - 它的直接子元素就会变成
flex items - 子元素的排列规则就不再主要按普通文档流决定,而是按
flex算法决定
例如:
css
.container {
display: flex;
}
这句话的意思不是"让这个容器自己变横着",而是:
让这个容器里面的子元素按 flex 规则布局。
三、Flex 的核心概念
要真正用好 flex,先把这几个概念吃透。
1. Flex 容器(flex container)
写了:
css
.container {
display: flex;
}
这个 .container 就是 Flex 容器。
2. Flex 项(flex items)
Flex 容器的直接子元素,就是 Flex 项。
例如:
ini
<div class="container">
<div class="item">A</div>
<div class="item">B</div>
<div class="item">C</div>
</div>
这里:
.container是 flex container- 三个
.item是 flex items
注意:
只有直接子元素才是 flex item。
孙元素不会自动成为 flex item。
3. 主轴(main axis)
Flex 布局里,最重要的是"轴"的概念。
主轴默认是横向。
也就是默认情况下,子元素从左到右排。
css
.container {
display: flex;
flex-direction: row;
}
这是默认值。
4. 交叉轴(cross axis)
和主轴垂直的方向,就是交叉轴。
如果主轴是横向:
- 主轴:左右
- 交叉轴:上下
如果主轴改成纵向:
css
.container {
display: flex;
flex-direction: column;
}
那就变成:
- 主轴:上下
- 交叉轴:左右
5. 主轴对齐 vs 交叉轴对齐
Flex 常见对齐问题,本质都在这两个点:
justify-content:控制主轴对齐align-items:控制交叉轴对齐
这个一定要记牢。
四、Flex 常用属性详解
1. display: flex
开启 flex 布局。
css
.container {
display: flex;
}
默认效果:
- 子元素横向排列
- 不自动换行
- 子元素会变成 flex items
2. flex-direction
控制主轴方向。
css
.container {
flex-direction: row;
}
可选值:
row:横向,从左到右row-reverse:横向,从右到左column:纵向,从上到下column-reverse:纵向,从下到上
示例:
css
.container {
display: flex;
flex-direction: column;
}
这时子元素会竖着排。
3. justify-content
控制主轴对齐。
常见值:
flex-startcenterflex-endspace-betweenspace-aroundspace-evenly
示例:
css
.container {
display: flex;
justify-content: center;
}
如果主轴是横向,表示水平居中。
css
.container {
display: flex;
justify-content: space-between;
}
表示两端对齐,中间均匀分开。
4. align-items
控制交叉轴对齐。
常见值:
stretch(默认)flex-startcenterflex-endbaseline
示例:
css
.container {
display: flex;
align-items: center;
}
如果主轴是横向,这通常表示垂直居中。
5. flex-wrap
控制是否换行。
默认:
css
flex-wrap: nowrap;
也就是不换行。
可选值:
nowrapwrapwrap-reverse
示例:
css
.container {
display: flex;
flex-wrap: wrap;
}
当子元素放不下时,会自动换到下一行。
6. gap
控制 flex 项之间的间距。
css
.container {
display: flex;
gap: 16px;
}
这通常比给子元素写 margin-right 更干净。
优点:
- 不用处理最后一个元素的 margin
- 横向、纵向、换行后的间距都更统一
7. align-content
这个属性很容易和 align-items 混。
它的作用是:
当 flex items 出现多行时,控制多行整体在交叉轴上的分布。
也就是说:
- 单行 flex 容器里,
align-content通常没意义 - 多行情况下才更明显
8. 子元素属性:flex-grow
控制子元素如何分配剩余空间。
css
.item {
flex-grow: 1;
}
意思是:
- 如果容器还有剩余空间
- 这个 item 可以参与瓜分
例子:
css
.item1 {
flex-grow: 1;
}
.item2 {
flex-grow: 2;
}
那么剩余空间会按 1:2 分。
9. 子元素属性:flex-shrink
控制空间不够时,子元素怎么缩小。
默认:
css
flex-shrink: 1;
意思是默认允许缩小。
如果不想缩小:
css
.item {
flex-shrink: 0;
}
这在某些固定宽度按钮、图标、头像中很常见。
10. 子元素属性:flex-basis
定义子元素在主轴方向上的基础尺寸。
css
.item {
flex-basis: 200px;
}
它比直接写 width 更符合 flex 的思维。
因为它表示:
在 flex 算法开始分配空间前,这个项目的"初始参考尺寸"。
11. flex 简写
这是最常见的写法之一。
css
.item {
flex: 1;
}
它通常可以理解为:
css
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0%;
这表示:
- 可以增长
- 可以收缩
- 初始基准为 0,重点按比例分剩余空间
常见几种写法:
css
flex: 1;
flex: auto;
flex: none;
flex: 0 0 200px;
其中最常见的是:
flex: 1
常用于多个子元素平分空间。
flex: 0 0 200px
表示:
- 不增长
- 不缩小
- 固定 200px
适合固定列宽、固定按钮宽度等场景。
12. align-self
让某个子元素单独覆盖 align-items。
css
.item {
align-self: flex-start;
}
适合个别元素单独调整。
五、Flex 最常见的实战场景
场景 1:水平居中 + 垂直居中
css
.container {
display: flex;
justify-content: center;
align-items: center;
}
这是最经典的 flex 场景。
场景 2:左右两端对齐
css
.header {
display: flex;
justify-content: space-between;
align-items: center;
}
适合:
- 导航栏
- 头部工具栏
- 列表项左右布局
场景 3:一排按钮
css
.actions {
display: flex;
gap: 12px;
}
场景 4:左边固定,右边自适应
css
.container {
display: flex;
}
.sidebar {
width: 240px;
flex-shrink: 0;
}
.content {
flex: 1;
}
非常常见。
场景 5:多行卡片流式排布
css
.list {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
如果只是简单"换行排卡片",flex 也能做。
但如果你明确是多行多列网格,通常 grid 更自然。
场景 6:把某个元素推到最右边
css
.container {
display: flex;
}
.right {
margin-left: auto;
}
这是 flex 很经典的用法。
六、Flex 常见问题与注意事项
这一部分是最重要的实战部分。
1. 为什么 flex: 1 有时候不生效?
最常见原因有几个:
原因 1:不是 flex item
只有父元素是 display: flex,它的直接子元素上写 flex: 1 才有意义。
原因 2:父元素没有剩余空间
flex-grow 是分"剩余空间"的。
如果本来就没空间,当然看不出效果。
原因 3:内容把元素撑住了
某些内容过长,默认最小尺寸限制会让元素没法按预期收缩或分配。
这时常见解决方式是:
arduino
.item {
min-width: 0;
}
这个点非常重要。
2. 为什么 flex 子元素经常被长文本撑爆?
这是 flex 最经典的坑之一。
例如:
css
.container {
display: flex;
}
.left {
width: 100px;
}
.right {
flex: 1;
}
.right 里面如果有一段特别长、不能换行的文字,布局可能会被撑爆。
原因通常不是 flex 坏了,而是:
flex item 默认的最小尺寸限制,不允许它比内容最小尺寸更小。
解决办法通常是:
css
.right {
flex: 1;
min-width: 0;
}
如果是纵向 flex 布局,类似问题则常常要写:
arduino
.child {
min-height: 0;
}
这个是实战必记点。
3. 为什么文本省略号 ellipsis 在 flex 里不生效?
你明明写了:
css
.title {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
但还是不生效,常见原因是 flex item 没有被允许缩小。
解决方式很常见是:
css
.title-wrap {
flex: 1;
min-width: 0;
}
然后内部文本再做省略。
4. 为什么子元素高度被拉满了?
因为 align-items 默认值是:
css
align-items: stretch;
这意味着在交叉轴方向上,子元素可能被自动拉伸。
如果不想这样,可以改成:
css
.container {
align-items: flex-start;
}
或者:
css
.container {
align-items: center;
}
5. 为什么 margin-top: auto / margin-left: auto 在 flex 里特别强?
因为在 flex 布局里,auto margin 可以吸收剩余空间。
比如:
css
.item {
margin-left: auto;
}
就能把自己推到最右边。
在纵向 flex 布局里:
css
.item {
margin-top: auto;
}
可以把元素推到底部。
这个比很多定位写法更优雅。
6. 为什么 width 和 flex-basis 看起来像打架?
在 flex item 上:
flex-basis是 flex 算法的初始参考尺寸width是普通盒模型尺寸属性
如果两者都写,实际布局常常以 flex 规则为主。
项目里建议:
- 如果这是 flex item,优先想
flex-basis - 不要一边复杂写
width,一边又写复杂的flex
7. 为什么纵向 flex 布局里滚动区域经常失效?
经典场景:
css
.page {
display: flex;
flex-direction: column;
height: 100vh;
}
.main {
flex: 1;
overflow: auto;
}
结果 .main 不能正常滚。
很多时候要补:
css
.main {
flex: 1;
min-height: 0;
overflow: auto;
}
原因和前面一样:
flex item 默认最小尺寸限制会影响收缩。
这个在页面布局里极其常见。
8. Flex 适合一维,不要硬拿来做复杂二维网格
虽然 flex + wrap 也能排多行卡片,但如果你明确要:
- 多列统一
- 行列都精确控制
- 网格对齐更稳定
那应该优先考虑 grid。
七、Grid 到底是什么
grid 是 网格布局(CSS Grid Layout) 。
它的核心思想是:
父元素先定义一个二维网格,然后子元素按网格的行和列进行布局。
和 flex 最大的区别在于:
flex更擅长一维grid更擅长二维
示例:
css
.container {
display: grid;
}
一旦开启 grid,子元素就会作为 grid items 按网格规则布局。
八、Grid 的核心概念
1. Grid 容器
写了:
css
.container {
display: grid;
}
这个元素就是 grid container。
2. Grid 项
它的直接子元素就是 grid items。
3. 轨道(tracks)
Grid 最核心的概念之一是轨道。
轨道就是:
- 行轨道(rows)
- 列轨道(columns)
例如:
css
.container {
display: grid;
grid-template-columns: 200px 1fr 1fr;
}
表示三列:
- 第一列 200px
- 第二列 1fr
- 第三列 1fr
4. 网格线(grid lines)
每一行每一列之间都有线,用来定义项目开始和结束位置。
5. 单元格(cell)
行和列交叉出的格子,就是单元格。
6. 区域(area)
多个单元格可以组成一个区域。
这使得 Grid 非常适合做页面骨架布局。
九、Grid 常用属性详解
1. display: grid
开启 grid 布局。
css
.container {
display: grid;
}
2. grid-template-columns
定义列轨道。
css
.container {
display: grid;
grid-template-columns: 200px 1fr 1fr;
}
常见写法:
css
grid-template-columns: repeat(3, 1fr);
表示三列,每列平分空间。
3. grid-template-rows
定义行轨道。
arduino
.container {
grid-template-rows: 60px auto 40px;
}
4. gap
定义网格间距。
css
.container {
display: grid;
gap: 16px;
}
也可以分开写:
css
row-gap: 16px;
column-gap: 24px;
5. repeat()
重复写法,非常常用。
css
grid-template-columns: repeat(4, 1fr);
比手写 1fr 1fr 1fr 1fr 更简洁。
6. fr 单位
fr 表示剩余空间的比例单位。
css
grid-template-columns: 1fr 2fr 1fr;
表示三列按 1:2:1 分剩余空间。
7. minmax()
定义最小值和最大值范围。
css
grid-template-columns: repeat(3, minmax(200px, 1fr));
意思是:
- 每列最小 200px
- 最大可以拉伸到 1fr
这是响应式网格里非常强大的工具。
8. auto-fit / auto-fill
这是 Grid 响应式卡片布局的经典写法。
css
.container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
}
效果通常是:
- 容器宽时,多放几列
- 容器窄时,列数自动减少
- 每列最小 200px,剩余空间均分
这是非常推荐掌握的写法。
9. grid-column / grid-row
控制某个项目跨几列、跨几行。
css
.item {
grid-column: 1 / 3;
}
表示从第 1 根列线到第 3 根列线,也就是横跨 2 列。
css
.item {
grid-row: 1 / 3;
}
表示跨 2 行。
10. grid-template-areas
给网格区域命名。
css
.container {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-areas:
"sidebar header"
"sidebar main";
}
子元素:
css
.sidebar {
grid-area: sidebar;
}
.header {
grid-area: header;
}
.main {
grid-area: main;
}
这对页面骨架布局很清晰。
11. justify-items / align-items
控制 grid item 在单元格里的对齐方式。
css
.container {
justify-items: center;
align-items: center;
}
12. justify-content / align-content
控制整个网格在容器中的分布。
这一组容易和 items 混。
可以这样记:
*-items:控制每个格子里的项目*-content:控制整个网格整体
十、Grid 最常见的实战场景
场景 1:等宽多列卡片
css
.list {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}
场景 2:响应式卡片墙
css
.list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 16px;
}
这是很推荐的现代写法。
场景 3:后台布局
css
.layout {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-rows: 64px 1fr;
grid-template-areas:
"sidebar header"
"sidebar main";
height: 100vh;
}
.sidebar {
grid-area: sidebar;
}
.header {
grid-area: header;
}
.main {
grid-area: main;
}
场景 4:某个卡片突出显示
css
.featured {
grid-column: span 2;
grid-row: span 2;
}
适合做精选卡片、推荐块。
十一、Grid 常见问题与注意事项
1. 为什么 1fr 有时候会被内容撑爆?
这是 Grid 的经典坑。
例如:
css
.container {
display: grid;
grid-template-columns: 200px 1fr;
}
右边列里如果有长内容,有时会把网格撑开。
因为 1fr 轨道不总是你想象中的"无脑自适应",它仍然会受内容最小尺寸影响。
一个很常见的解决方式是:
css
grid-template-columns: 200px minmax(0, 1fr);
这个非常重要。
你会发现:
minmax(0, 1fr)在很多实战里比直接1fr更稳
2. 为什么文本省略号在 Grid 里也不生效?
原因和 flex 很像:
默认最小尺寸限制导致项目无法缩小到触发省略。
常见解决方式:
arduino
.item {
min-width: 0;
}
或者轨道上直接用:
css
grid-template-columns: minmax(0, 1fr);
3. auto-fit 和 auto-fill 有什么区别?
这是 Grid 最常见的疑问之一。
可以先用最直观方式理解:
auto-fill:尽可能"填充"更多轨道,即使有的轨道可能空着auto-fit:更倾向把已有项目拉伸去占满空间
在实际卡片响应式布局中:
大多数时候你更想用
auto-fit
因为视觉上更自然。
4. 为什么 grid-template-areas 很好看,但有时不灵活?
它非常适合:
- 页面骨架
- 结构区域明确的布局
但如果项目数量动态变化很多,或者布局高度不规则,可能就不如直接写列规则灵活。
所以不要什么都上 areas。
5. 不要把 Grid 当成"所有布局的唯一答案"
Grid 很强,但不是所有场景都该用 Grid。
例如:
- 一排按钮
- 一个 header 左右对齐
- 一个弹窗内容垂直居中
这类通常 Flex 更自然。
6. Grid 中项目的高度对齐要小心内容不一致
你写卡片网格时,经常会遇到:
- 某些卡片内容长
- 某些卡片内容短
- 结果整体看起来参差不齐
这时要想清楚你要的是:
- 自然高度
- 还是统一高度
如果要统一高度,通常还需要对卡片内部再配合 flex。
这也是 Grid 和 Flex 经常组合使用的原因。
7. Grid 适合大布局,内部微调常常还得交给 Flex
Grid 负责:
- 宏观分格子
Flex 负责:
- 格子内部对齐
- 按钮分布
- 文本与图标排列
这是非常典型的实战组合。
十二、Flex 和 Grid 怎么配合使用
实际项目里,最常见的不是二选一,而是配合用。
典型组合方式
1. 页面整体用 Grid
例如:
- 左侧菜单
- 顶部导航
- 主内容区
2. 局部操作区用 Flex
例如:
- header 里的左右布局
- 卡片底部按钮组
- 列表项操作按钮
3. 卡片列表用 Grid,卡片内部用 Flex
例如:
css
.card-list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 16px;
}
.card {
display: flex;
flex-direction: column;
}
这样:
- 外部网格排布由 Grid 负责
- 卡片内部上下结构由 Flex 负责
这是非常现代、非常推荐的写法。
十三、一张表总结核心区别
| 对比项 | Flex | Grid |
|---|---|---|
| 维度 | 一维 | 二维 |
| 更适合 | 一行/一列排列 | 多行多列网格 |
| 主体思路 | 沿主轴分配与对齐 | 定义行列轨道 |
| 典型场景 | 导航、按钮组、居中、工具栏 | 卡片墙、后台布局、页面骨架 |
| 常见坑 | min-width: 0、伸缩、溢出 |
minmax(0, 1fr)、内容撑爆、区域规划 |
| 最佳搭档 | Grid | Flex |
十四、结论
如果要把 Flex 和 Grid 真正用顺,最重要的不是死背属性,而是建立这两个思维:
Flex 思维
父容器控制子元素沿一个方向如何排列、对齐、伸缩。
Grid 思维
父容器先定义二维网格,再让子元素落入网格。
最后再压缩成最实用的开发建议:
- 一维问题先想 Flex
- 二维问题先想 Grid
- 复杂页面通常是 Grid + Flex 组合
- Flex 常记
min-width: 0/min-height: 0 - Grid 常记
minmax(0, 1fr) - 不要只盯子元素,先看父容器的布局上下文