"这个布局我该用 Flexbox 还是 Grid?" —— 这大概是前端写样式时最高频的一次纠结。网上很多文章把它俩讲得像是势不两立、要二选一,搞得不少人莫名其妙站了队。其实根本不是这样:Flexbox 和 Grid 是互补的两把工具,理解了各自的"心智模型",你就能在三秒内知道任何一个布局该用哪个,以及很多时候 —— 两个一起用。
这篇会从两者各自的设计思路讲起,给一棵能直接照着走的决策树,再过一遍真实项目里的常见布局模式、嵌套配合的写法、容易踩的坑,以及一份 FAQ。看完之后,"该用哪个"对你来说不再是凭感觉,而是能推理出来的。
一句话先给答案
布局是"一个方向上的排列" —— 用 Flexbox;布局是"行和列同时都要管" —— 用 Grid。
Flexbox 是一维布局工具:它一次只专心管一个方向,要么是一行,要么是一列。Grid 是二维布局工具:它同时管行和列,适合搭整体骨架。记住这个"一维 vs 二维"的分界,90% 的选择问题当场就解决了。剩下的 10%,是一些需要经验的判断,这篇后面会讲。下面先把两者各自的强项展开。
Flexbox:一维布局的王者
Flexbox 解决的是"一组元素在某一个方向上怎么排"的问题:怎么对齐、怎么分配剩余空间、怎么换行。它的子元素是"流动"的 —— 会根据内容和可用空间自动伸缩。
/* Flexbox:一行导航,左 logo 右菜单,垂直居中 */
.navbar {
display: flex;
justify-content: space-between; /* 主轴:两端对齐 */
align-items: center; /* 交叉轴:垂直居中 */
}
理解 Flexbox,关键是建立"一根轴"的心智模型:
/* Flexbox 的核心心智模型:一根"轴" */ 主轴(main axis)──────────────────────────────► │ [子项] [子项] [子项] ← 子项沿主轴排列 │ 交叉轴(cross axis) flex-direction 决定主轴方向(row 横 / column 竖) justify-content 管主轴上怎么分布(flex-start/center/space-between...) align-items 管交叉轴上怎么对齐(flex-start/center/stretch...) flex: 1 让子项"吃掉"主轴上的剩余空间 flex-wrap 空间不够时是否换行
所有 Flexbox 的属性,都是围绕这根轴在工作。flex-direction 决定主轴是横是竖;justify-content 管"主轴方向上,子项之间怎么分布";align-items 管"交叉轴方向上,子项怎么对齐";flex: 1 让子项去"吃掉"主轴上的剩余空间;flex-wrap 决定空间不够时换不换行。把这几个吃透,日常九成的一维布局都不在话下。
Flexbox 最舒服的场景:导航栏、工具栏、按钮组、标签组、表单的一行(标签 + 输入框)、卡片内部的图文排列、还有那个万年需求 —— 把一个元素水平垂直居中(display:flex; justify-content:center; align-items:center; 三行搞定)。只要你的需求能用"把这一排/这一列里的东西对齐、分配空间"描述清楚,Flexbox 就是最顺手的工具。
Flexbox 还有一个常被忽略的特性:它特别擅长处理"内容驱动"的布局 —— 子项的尺寸由内容自然决定,Flexbox 在这个基础上做对齐和空间分配。所以"这几个按钮,文字多的就宽一点,然后整体右对齐"这种需求,Flexbox 天生就会,不用你算。
Grid:二维布局的标准答案
Grid 解决的是"整个区域怎么划分成行和列、元素放进哪个格子"的问题。它的思路和 Flexbox 完全相反:Flexbox 是"元素自己流动",Grid 是先把网格画好,再把元素往格子里摆。
/* Grid:一个经典的"圣杯"页面骨架 */
.layout {
display: grid;
grid-template-columns: 200px 1fr 200px; /* 左栏 主区 右栏 */
grid-template-rows: 60px 1fr 80px; /* 头 内容 脚 */
grid-template-areas:
"header header header"
"sidebar main aside"
"footer footer footer";
min-height: 100vh;
}
.header { grid-area: header; }
.sidebar{ grid-area: sidebar; }
.main { grid-area: main; }
.aside { grid-area: aside; }
.footer { grid-area: footer; }
注意 grid-template-areas 这个写法 —— 你几乎是在用 ASCII 把页面布局"画"出来,哪块是 header、哪块是 sidebar 一目了然,可读性极高。改布局的时候,改这几行字符画就行,不用动一堆定位代码。
Grid 还有两个 Flexbox 给不了的"超能力",值得单独说:
/* Grid 的两个超能力 */
/* 1. fr 单位 + repeat:优雅地等分 */
.cols { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
/* 三等分,自带间距,不用算百分比、不用处理 margin */
/* 2. auto-fill / auto-fit + minmax:数量不固定也能自动铺满换行 */
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 16px;
}
/* 每个卡片最小 220px,容器够宽就多放几列、不够就自动换行 ——
不写一行媒体查询,响应式就成了。这是 Grid 的招牌技能。 */
第一个是 fr 单位配合 repeat() —— "三等分"过去要算 33.33%、还要处理 margin 带来的误差,现在 repeat(3, 1fr) 一行解决,gap 自带间距。第二个更厉害:repeat(auto-fill, minmax(220px, 1fr)) —— 这一行能实现"卡片最小 220px,容器宽就多放几列、窄就自动换行"的效果,不写一行媒体查询,响应式卡片墙就成了。这是 Grid 的招牌技能,Flexbox 做不到这么干净。
Grid 最适合的场景:整页骨架(头/侧/主/脚)、图片画廊、卡片墙、仪表盘、表格状的规整布局、任何"行列都要对齐"的地方。还有一个 Flexbox 完全做不到的能力 —— 元素可以精确地跨行、跨列(grid-column: span 2),做出那种错落有致的杂志式排版。
决策树:遇到具体布局怎么选
把上面的理解浓缩成一棵决策树,遇到任何布局,照着走:
决策树:遇到一个布局,先问自己——
这个布局,我描述它的时候,提到了"几行几列"吗?
│
├─ 没有,只是"一排东西" / "一列东西" ──► 用 Flexbox
│ (导航、按钮组、表单一行、卡片内部图文)
│
└─ 有,"行和列都要对齐" ──► 用 Grid
(整页骨架、卡片墙、表格状布局、仪表盘)
还有两个加分判断:
· 元素需要精确"跨行 / 跨列" ──► 只能用 Grid
· 内容数量不固定、要自动换行铺满 ──► 用 Grid 的 auto-fill
有个特别简单、特别好用的自检方法:当你在脑子里(或对同事)描述这个布局时,如果你只提到了"横"或"竖"一个方向 —— 用 Flexbox;如果你提到了"几行几列" —— 用 Grid。语言会出卖你的真实需求。
常见布局模式对照表
| 布局 | 用 | 为什么 |
|---|---|---|
| 顶部导航栏 / 工具栏 | Flexbox | 本质是一行元素的对齐与分布 |
| 整页骨架(头/侧/主/脚) | Grid | 行和列都要管,areas 写法一目了然 |
| 按钮组 / 标签组 | Flexbox | 一维排列,可能需要换行 |
| 图片画廊 / 卡片墙 | Grid | 规整的行列对齐,等宽等高轻松实现 |
| 响应式卡片(数量不定、自动换行) | Grid(auto-fill) | repeat(auto-fill, minmax()) 一行搞定,免媒体查询 |
| 垂直水平居中一个元素 | 都行 | Flex 三行;Grid 用 place-items:center 更短 |
| 表单一行(标签 + 输入框) | Flexbox | 一维,要按内容伸缩 |
| 表单整体(多行标签对齐) | Grid | 标签列和输入列要对齐,二维 |
| 元素需要跨行 / 跨列 | Grid | Flexbox 做不到精确跨格 |
| media 对象(头像 + 一段文字) | Flexbox | 经典一维:固定宽的头像 + fl:1 的文字 |
| 仪表盘 / 后台卡片网格 | Grid | 规整网格 + 部分卡片跨格 |
| "撑满剩余空间"的侧边栏布局 | 都行 | Flex: 固定宽 + flex:1;Grid: 固定列 + 1fr |
它们不是竞争关系,是配合关系
真实项目里,最常见的不是"二选一",而是嵌套使用:外层用 Grid 搭页面大骨架,每个格子内部该用 Flex 就用 Flex。
/* 外层用 Grid 搭页面骨架,某个格子内部用 Flex 排小元素 */
.page {
display: grid;
grid-template-columns: 240px 1fr; /* 侧边栏 + 主区 */
}
.toolbar { /* 这是 Grid 的一个子项,内部又是 Flex */
display: flex;
gap: 12px;
align-items: center;
justify-content: space-between;
}
这种"Grid 搭骨架、Flex 填内容"的组合,是现代前端布局的标准做法,也是经验丰富的开发者的肌肉记忆。再举几个嵌套的典型例子:一个卡片墙 —— 外层 Grid 排卡片,每张卡片内部 Flex 排"图 + 标题 + 摘要 + 底部操作栏";一个后台页面 —— 外层 Grid 分"侧边栏 + 主区",主区里的工具栏用 Flex,工具栏下面的数据网格又是 Grid。
所以别再纠结"我是 Flex 派还是 Grid 派"了 —— 成熟的写法是两个都用,各管各擅长的那一层。问"用哪个"的时候,正确的问法是"这一层布局用哪个",而不是"整个页面用哪个"。
对齐属性:两套体系,但越来越像
一个让很多人困惑的点:Flexbox 和 Grid 都有 justify-* 和 align-* 系列属性,但含义不完全一样。
在 Flexbox 里:justify-content 管主轴,align-items 管交叉轴 —— 主轴是横是竖取决于 flex-direction,所以这两个属性的"实际方向"是会变的,这是 Flexbox 对齐最容易绕晕人的地方。
在 Grid 里:justify-* 永远管行内方向(横),align-* 永远管块方向(竖),方向是固定的,不会跟着什么属性变 —— 这一点 Grid 反而比 Flexbox 直观。另外 Grid 还区分 justify-items(管所有格子里内容的对齐)和 justify-content(管整个网格在容器里的对齐),Flexbox 没有这个区分。
不用死记,知道"两套体系存在差异、写的时候看一眼即可"就行。一个实用提示:想让单个 Grid 元素自己对齐,用 justify-self / align-self;想让单个 Flex 元素自己对齐(交叉轴),用 align-self。
几个常见误区
误区一:用 Flexbox 硬做二维网格。给 flex 容器加 flex-wrap: wrap 确实能挤出多行效果,但各行之间的"列"是对不齐的 —— 因为 Flexbox 每一行是独立排布的,它不知道也不关心上一行的列宽。真要规整的网格,老老实实用 Grid。这是最常见、也最该改掉的一个习惯。
误区二:觉得 Grid 太复杂、不敢用。Grid 的基础(grid-template-columns + gap)其实比想象中简单,grid-template-columns: repeat(3, 1fr) 一行就是三等分。复杂的 grid-template-areas 是进阶,不用一上来就学全 —— 先用基础的,慢慢就会了。因为"不熟"而绕开 Grid,等于放弃了它那两个超能力。
误区三:还在用 float 或绝对定位做布局。2026 年了,float 是用来做"文字绕排图片"的,绝对定位是用来做悬浮层、tooltip 的 —— 它们都不该再是"布局主力"。如果你还在用 float + clearfix 搭多栏布局,是时候彻底换掉了。
误区四:滥用 Grid 的精确定位。Grid 能让你把元素精确放到某行某列,但如果一个布局其实是"流动"的、子项数量会变,你却用 grid-row: 2 / 4; grid-column: 1 / 3; 把每个都钉死,改起来会很痛苦。能用 grid-template-areas 或自动流(auto-placement)的,就别手动钉位置。
浏览器兼容性
2026 年的今天,Flexbox 和 Grid 的兼容性都不再是问题 —— 所有主流浏览器的现代版本全部完整支持,连子网格(subgrid,Grid 的一个进阶特性,让嵌套的网格能对齐到父网格的轨道)也已经被主流浏览器支持了。
除非你的项目还要硬扛 IE 11 这种古董(2026 年基本不会了),否则放心用,不需要任何 polyfill,也不需要写带浏览器前缀的旧语法。如果确实要兼容很老的环境,优先保证 Flexbox(它出现得更早、老浏览器支持更好),Grid 的部分用 @supports (display: grid) 做渐进增强。
FAQ
居中一个元素,到底用哪个?都行。Flexbox:display:flex; justify-content:center; align-items:center;。Grid 更短:display:grid; place-items:center; 一行。看个人习惯,没有对错。
gap 属性是 Grid 专属的吗?不是。gap 最早出现在 Grid 里,但现在 Flexbox 也完全支持 gap 了。所以"用 Flex 排一组元素、它们之间要有间距",直接 gap 就行,不用再给每个子项加 margin 然后处理首尾元素的边距问题 —— 这是个很多人没更新的旧知识。
Grid 的 fr 和百分比有什么区别?fr 分配的是"剩余空间",会自动考虑 gap;百分比是相对容器总宽度的,你用 33.33% + gap 就会溢出,得自己算。fr 是为 Grid 量身做的单位,优先用它。
子项的 min-width 会让 Flex/Grid 子项"压不下去",怎么办?这是个高频坑。Flex 和 Grid 子项默认有一个"最小内容尺寸",内容(比如一段不换行的长文字、一个图片)会撑着它不让它再小。解法是给子项加 min-width: 0(Flex)或 min-width: 0 / overflow: hidden,允许它被压缩。这个坑几乎每个人都踩过。
响应式布局,媒体查询还需要吗?需要,但用得更少了。repeat(auto-fill, minmax()) 能干掉一大批"卡片墙"类的媒体查询;CSS 容器查询能让组件自适应。但"页面级的大改版"(比如窄屏时整体从三栏变单栏、导航收起)还是媒体查询的活。它们是配合关系。
深入 Flexbox:flex 简写的三个属性
前面说 flex: 1 能"吃掉剩余空间",但 flex 其实是三个属性的简写,搞懂这三个,你才算真正会用 Flexbox 而不是"碰运气":
flex 是三个属性的简写:flex: <grow> <shrink> <basis>
flex-grow: 有剩余空间时,按这个比例去"抢"。0=不抢,1=参与瓜分
flex-shrink: 空间不够时,按这个比例被"压缩"。0=死活不压,1=参与压缩
flex-basis: 分配前的"基准尺寸",auto=看 width/内容
常见三种写法的含义:
flex: 1; → 1 1 0 "和别人等分剩余空间"(最常用)
flex: auto; → 1 1 auto "按自身内容大小做基准,再瓜分剩余"
flex: none; → 0 0 auto "固定尺寸,既不抢也不压"
flex: 0 0 200px;→ "固定 200px,雷打不动"(做固定宽侧边栏)
flex-grow 决定"有多的空间时怎么分":值是一个比例,0 表示"我不参与抢",1 表示"我参与等分",2 表示"我抢的是别人的两倍"。flex-shrink 决定"空间不够时怎么压":0 表示"我死活不被压缩"(做固定宽元素时特别有用),1 表示"我参与被压"。flex-basis 是"瓜分/压缩之前的基准尺寸"。
实战里最值得记住的是这几个组合:想让几个元素等分剩余空间,用 flex: 1;想要一个固定宽、绝不变形的侧边栏或图标,用 flex: 0 0 200px(或 flex: none 配合 width);想让元素"内容多就宽、但也参与瓜分",用 flex: auto。很多"Flexbox 布局诡异变形"的问题,根源都是没搞清 flex-shrink 默认是 1 —— 元素会在空间不够时被悄悄压扁,而你以为它会保持原尺寸。
深入 Grid:显式网格与隐式网格
Grid 还有一组容易被忽略、但很实用的概念:显式网格和隐式网格。
显式网格 vs 隐式网格: 你用 grid-template-columns / rows 明确定义的,是【显式网格】。 当子项数量超过了显式定义的格子,Grid 会自动多生成行/列来放 —— 这些自动生成的,是【隐式网格】。 grid-auto-rows: 120px; /* 隐式行的高度 */ grid-auto-flow: row; /* 隐式项的排列方向(默认 row) */ grid-auto-flow: dense; /* 紧凑模式:让后面的小项回填前面的空洞 */ 实战:你只定义"3 列",不定义行数,让内容有多少自动撑多少行 —— grid-template-columns: repeat(3, 1fr); grid-auto-rows: minmax(100px, auto); /* 行高至少 100,内容多就自动长 */
简单说:你用 grid-template-columns/rows 明确画出来的格子是显式网格;当子项的数量超出了你画的格子,Grid 会自动生成额外的行或列来安放它们,这些自动生成的就是隐式网格。
这个机制在实战里非常有用:你常常只想定义"几列",而行数让内容自己决定 —— 这时候就用 grid-template-columns 定义列,用 grid-auto-rows 控制自动生成的行的高度。grid-auto-flow: dense 还能开启"紧凑回填"模式,让后面的小项去填补前面因为跨格留下的空洞,适合做瀑布流式的不规则网格。理解了显式/隐式,你就能写出"列数固定、行数自适应"的健壮网格,而不是把每一行都写死。
写在最后
Flexbox 和 Grid 不是要你站队的两个阵营,而是工具箱里互补的两把工具:Flexbox 管"一维",Grid 管"二维";Grid 搭骨架,Flex 填内容。
把这套逻辑刻进肌肉记忆 —— 描述布局时只提一个方向就用 Flex、提到行列就用 Grid、需要跨格或自动换行铺满就用 Grid、复杂布局就两个嵌套用 —— 你写布局的速度和质量都会上一个台阶。不再是"试一下这个属性看看效果",而是"我清楚地知道这个布局该用什么、怎么搭"。
布局是前端的基本功,而 Flexbox + Grid 就是 2026 年这门基本功的全部答案。把这两把工具用熟、用对,你就再也不会对着一个设计稿发愁"这该怎么排"了。
—— 别看了 · 2026