全文约2200字,预计阅读8分钟。
页面挤成一团?换行错位?居中怎么都对不齐?做过两三个后台页面之后,基本都会和这几个问题反复交手。Flex 解决了一维排列,Grid 补上了二维网格,但这两套布局写起来都有不少"看起来对、跑起来歪"的坑。这篇文章把日常最常写的几种布局串一遍,附上代码和踩坑记录,尽量让你下次少调两小时 DevTools。
01 前言:为什么布局要从 Flex 说到 Grid
Flexbox 面向一维方向:一行或者一列,负责"这一排元素怎么排"。Grid 面向二维网格:行和列同时定义,负责"整个页面骨架怎么切"。两者不是替代关系,而是分工------页面大骨架用 Grid,局部组件用 Flex。
很多人刚学的时候把 Grid 当"更强的 Flex",结果写出来的东西又长又难维护。正确的思路是先看这堆元素是一维关系还是二维关系,再选模型。
02 常见布局诉求先对号入座
日常需求大致就这几类:
- 顶部一条导航,左边 logo、右边菜单:一维横排,Flex;
- 卡片列表一行三张、自动换行:一维排列,Flex 或 Grid 都行;
- 整页分为 header / aside / main / footer:二维骨架,Grid;
- 表单里 label 和 input 两列对齐:二维网格,Grid;
- 弹窗内容水平垂直居中:Flex 三行代码搞定。
先判断维度,再开写,能省掉一半返工。
03 Flex 布局核心与三个高频踩坑
3.1 容器与项目两条轴
给父元素加 display: flex 之后,它的直接子元素就变成 flex item。主轴由 flex-direction 决定,交叉轴与之垂直。常用属性分两组记:
css
.container {
display: flex;
justify-content: center; /* 主轴对齐 */
align-items: center; /* 交叉轴对齐 */
gap: 12px; /* 项与项间距 */
}
记住一个口诀:justify 管主轴,align 管交叉轴。写反的时候页面不会报错,只是怎么调都不对。
3.2 经典水平垂直居中
css
.center {
display: flex;
justify-content: center;
align-items: center;
}
三行代码搞定,比以前套 position: absolute 加 transform 稳得多,而且内容尺寸变化时不会偏。
3.3 踩坑一:flex: 1 不是 width: 100%
新手常写:
css
.item { flex: 1; }
然后发现内容多的时候元素被挤变形。原因是 flex: 1 等于 flex-grow: 1; flex-shrink: 1; flex-basis: 0%,它允许元素收缩。内容撑不开时想让它均分,应该写:
css
.item { flex: 1 1 0; min-width: 0; }
3.4 踩坑二:min-width: auto 导致内容溢出
flex item 的 min-width 默认是 auto,意思是"最小不能小于内容宽度"。子元素里放一段长文本或者一个表格,整排就会溢出容器。修复方式很直接:
css
.item { min-width: 0; }
加完之后文本才会正常换行,而不是把兄弟元素顶出屏幕。
3.5 踩坑三:gap 出来之后就别再用 margin 凑间距
老代码里常见 margin-right: 12px 然后给末项清零。gap 普及之后直接写:
css
.row { display: flex; gap: 12px; }
不需要 :last-child 清零,换行时也不会多出半截间距。
3.6 换行场景记得 flex-wrap
一排按钮在窄屏被挤变形,多半是漏了 flex-wrap:
css
.toolbar {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
不写的话,主轴空间不够时子元素会按 flex-shrink 压缩,文字标签直接挤成两行。写成 wrap 之后放不下的项自动换到下一行,配合 gap 间距依然均匀。
Flex 与 Grid 的常见差异可以对照看:
| 维度 | Flex | Grid |
|---|---|---|
| 方向 | 一维,一条主轴 | 二维,行和列同时定义 |
| 间距 | gap 兼容良好 | gap 支持行列双向 |
| 适合 | 导航、按钮组、列表项 | 页面骨架、表单、仪表盘 |
| 内容驱动 | 由内容决定尺寸 | 由轨道定义决定尺寸 |
04 Grid 布局核心与三个高频踩坑
4.1 二维网格思维
Grid 的核心是"先画网格,再放东西":
css
.page {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-rows: 60px 1fr 40px;
grid-template-areas:
"header header"
"aside main"
"footer footer";
gap: 12px;
}
.header { grid-area: header; }
.aside { grid-area: aside; }
.main { grid-area: main; }
.footer { grid-area: footer; }
页面骨架用 grid-template-areas 写,结构一眼能读懂,比嵌套三层 div 清爽。
4.2 repeat、fr 与 minmax
写卡片列表时,常用这套组合:
css
.cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 16px;
}
含义是:每列最小 240px,剩余空间按 fr 均分,装得下几列就放几列。
4.3 踩坑四:auto-fill 和 auto-fit 弄混
两者在子项数量少的时候差异明显:
auto-fill:轨道先把列排满,剩下的空轨道保留;auto-fit:空轨道折叠,现有子项被拉伸撑满整行。
一行两个卡片时,auto-fit 会把两个卡片拉得很宽,auto-fill 则保持 240px 宽、右侧留白。想让卡片始终保持设计宽度用 auto-fill,想让少量卡片撑满用 auto-fit。
4.4 踩坑五:gap 和 margin 别叠加
Grid 里 gap 已经控制了行列间距,再给 item 加 margin 会出现间距翻倍,而且换行处很难看。统一用 gap,个别特殊项再单独微调。
4.5 踩坑六:1fr 不等于剩余空间均分
grid-template-columns: 1fr 1fr 看上去均分,但如果其中一列内容特别长,1fr 实际是 minmax(auto, 1fr),长内容会把这列撑宽。需要严格等宽时写:
css
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
4.6 实战:用 Grid 写一个经典后台骨架
下面这段是后台页面最常见的骨架,可以直接抄去改:
css
.admin {
display: grid;
grid-template-columns: 220px minmax(0, 1fr);
grid-template-rows: 56px minmax(0, 1fr);
min-height: 100vh;
}
.admin .topbar { grid-column: 1 / -1; }
.admin .sidebar { overflow: auto; }
.admin .content { overflow: auto; padding: 16px; }
要点是中间内容区用 minmax(0, 1fr) 而不是 1fr,否则里面的表格或者横向滚动区会把整列撑宽,顶栏错位。窄屏下再用媒体查询把侧边栏收成顶部抽屉:
css
@media (max-width: 768px) {
.admin { grid-template-columns: 1fr; }
.admin .sidebar { display: none; }
}
05 Flex 和 Grid 怎么选
一个判断流程:
- 元素是排成一条线(横或竖),用 Flex;
- 元素要同时按行和列对齐(表单、仪表盘、整页骨架),用 Grid;
- 不确定时,先写 Flex 跑通,局部二维区域再换 Grid,不要一步到位全 Grid。
实践中常见的组合是:外层 Grid 切页面大块,内层 Flex 排按钮、排菜单项。两者混用完全合法。
06 兼容性速查
现代浏览器对 Flex 的支持已经非常完善,Grid 自 Chrome 57、Safari 10.1 起全量支持 gap、grid-template-areas 等核心特性。如果项目还要兼容很老的机型,在引入新特性前先查目标浏览器版本矩阵,并在真实设备上跑一遍窄屏布局。
07 总结
- 一维排列用 Flex,二维骨架用 Grid,别用反;
- flex 容器上
justify和align管的是不同轴,写反是最常见 bug; - flex item 记得
min-width: 0,长文本才不会溢出; - Grid 里
auto-fill保宽、auto-fit撑满; - 间距统一交给
gap,少写:last-child清零。
布局没有银弹,把这几个高频坑记牢,日常写页面会顺很多。下次写之前先花十秒钟判断维度,再挑模型,比写完之后来回改样式要省心得多。