深入理解 Flex 与 Grid 布局
全部属性详解、经典布局方案、Subgrid、容器查询与实战技巧
什么是 Flex 与 Grid 布局?
定义:Flexbox(弹性盒布局)是 CSS3 引入的一维布局模型,擅长在单行或单列方向上分配空间和对齐元素。Grid(网格布局)是 CSS 的二维布局模型,可以同时控制行和列,适合构建复杂的页面整体结构。两者都会为子元素创建新的格式化上下文(FFC / GFC)。
涉及场景:
- 导航栏 / 工具栏:Flex 实现水平排列、间距均分、垂直居中
- 卡片列表:Flex
wrap或 Gridauto-fill实现响应式多列卡片 - 圣杯 / 双飞翼布局:Grid 几行代码替代传统浮动方案
- Dashboard 面板:Grid 的命名区域(
grid-template-areas)实现仪表盘布局 - 表单对齐:Grid 让 label 和 input 完美对齐
- 垂直居中:
display: flex; align-items: center; justify-content: center一行居中
作用:
- 告别浮动时代:Flex 和 Grid 彻底替代了
float+clearfix的布局方式 - 简化响应式:配合
minmax()、auto-fill、容器查询,少量代码实现自适应 - 精确控制:对齐、间距、比例分配、自动填充都有专用属性,不再需要 hack
- 面试必考:Flex 的
flex: 1含义、Grid 的fr单位是高频考题
Flexbox 完整指南
容器属性(父元素)
css
.container {
display: flex; /* 或 inline-flex */
/* 1. flex-direction:主轴方向 */
flex-direction: row; /* 默认:水平从左到右 */
flex-direction: row-reverse; /* 水平从右到左 */
flex-direction: column; /* 垂直从上到下 */
flex-direction: column-reverse; /* 垂直从下到上 */
/* 2. flex-wrap:是否换行 */
flex-wrap: nowrap; /* 默认:不换行(可能压缩子元素) */
flex-wrap: wrap; /* 换行 */
flex-wrap: wrap-reverse; /* 反向换行 */
/* 3. flex-flow:direction + wrap 简写 */
flex-flow: row wrap;
/* 4. justify-content:主轴对齐 */
justify-content: flex-start; /* 默认:起点对齐 */
justify-content: flex-end; /* 终点对齐 */
justify-content: center; /* 居中 */
justify-content: space-between; /* 两端对齐,间距相等 */
justify-content: space-around; /* 每个元素两侧间距相等 */
justify-content: space-evenly; /* 所有间距完全相等 */
/* 5. align-items:交叉轴对齐(单行) */
align-items: stretch; /* 默认:拉伸填满 */
align-items: flex-start; /* 交叉轴起点 */
align-items: flex-end; /* 交叉轴终点 */
align-items: center; /* 交叉轴居中 */
align-items: baseline; /* 基线对齐 */
/* 6. align-content:多行对齐(仅 wrap 时有效) */
align-content: stretch; /* 默认 */
align-content: flex-start;
align-content: flex-end;
align-content: center;
align-content: space-between;
align-content: space-around;
align-content: space-evenly;
/* 7. gap:间距(替代 margin) */
gap: 10px; /* 行列间距相同 */
gap: 10px 20px; /* 行间距 列间距 */
row-gap: 10px;
column-gap: 20px;
}子元素属性
css
.item {
/* 1. order:排列顺序(默认0,越小越前) */
order: -1; /* 排到最前面 */
order: 1; /* 排到后面 */
/* 2. flex-grow:放大比例(默认0,不放大) */
flex-grow: 0; /* 默认:不占据剩余空间 */
flex-grow: 1; /* 等比例分配剩余空间 */
flex-grow: 2; /* 分配的空间是 flex-grow:1 的两倍 */
/* 3. flex-shrink:缩小比例(默认1,等比例缩小) */
flex-shrink: 0; /* 不缩小(常用于固定宽度元素) */
flex-shrink: 1; /* 默认:等比例缩小 */
/* 4. flex-basis:初始大小(默认auto) */
flex-basis: auto; /* 默认:使用 width/height */
flex-basis: 0; /* 忽略内容大小,完全按 grow 分配 */
flex-basis: 200px; /* 固定初始宽度 */
flex-basis: 30%; /* 百分比 */
/* 5. flex:简写(推荐使用简写) */
flex: 0 1 auto; /* 默认:不放大,可缩小,自动大小 */
flex: 1; /* = flex: 1 1 0%(等分空间) */
flex: auto; /* = flex: 1 1 auto */
flex: none; /* = flex: 0 0 auto(固定大小) */
flex: 0 0 200px; /* 固定200px,不伸缩 */
/* 6. align-self:单个元素的交叉轴对齐 */
align-self: auto; /* 默认:继承 align-items */
align-self: flex-start;
align-self: flex-end;
align-self: center;
align-self: stretch;
align-self: baseline;
}flex 计算过程
css
/*
剩余空间 = 容器大小 - 所有 flex-basis 之和
flex-grow 分配:
每个元素获得 = (剩余空间 > 0) ? 剩余空间 * (自身grow / 总grow) : 0
最终大小 = flex-basis + 获得的空间
flex-shrink 分配(空间不足时):
需要缩小的总量 = |剩余空间|
每个元素缩小 = 需要缩小总量 * (自身shrink * basis / Σ(shrink_i * basis_i))
最终大小 = flex-basis - 缩小量
*/
/* 示例:容器 600px,三个子元素 */
.container { width: 600px; display: flex; }
.a { flex: 1 1 100px; } /* basis: 100px, grow: 1 */
.b { flex: 2 1 100px; } /* basis: 100px, grow: 2 */
.c { flex: 1 1 200px; } /* basis: 200px, grow: 1 */
/*
剩余空间 = 600 - (100 + 100 + 200) = 200px
总 grow = 1 + 2 + 1 = 4
.a = 100 + 200 * (1/4) = 100 + 50 = 150px
.b = 100 + 200 * (2/4) = 100 + 100 = 200px
.c = 200 + 200 * (1/4) = 200 + 50 = 250px
总计 = 600px ✅
*/Grid 完整指南
容器属性
css
.grid {
display: grid; /* 或 inline-grid */
/* 1. 定义列和行 */
grid-template-columns: 200px 1fr 200px; /* 三列 */
grid-template-columns: repeat(3, 1fr); /* 三等分 */
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); /* 自动填充 */
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); /* 自动适应 */
grid-template-columns: [start] 1fr [mid] 2fr [end]; /* 命名网格线 */
grid-template-rows: 60px 1fr 40px; /* 头-主体-底部 */
grid-template-rows: repeat(3, minmax(100px, auto)); /* 最小100px */
/* 2. 区域命名 */
grid-template-areas:
"header header header"
"sidebar content content"
"footer footer footer";
/* 3. 间距 */
gap: 10px 20px; /* 行间距 列间距 */
row-gap: 10px;
column-gap: 20px;
/* 4. 隐式网格(超出模板的自动行列) */
grid-auto-rows: minmax(100px, auto); /* 自动行的高度 */
grid-auto-columns: 1fr;
grid-auto-flow: row; /* 默认:先填行 */
grid-auto-flow: column; /* 先填列 */
grid-auto-flow: dense; /* 紧密填充(填补空洞) */
/* 5. 对齐 */
justify-items: stretch; /* 单元格内水平对齐(默认拉伸) */
align-items: stretch; /* 单元格内垂直对齐 */
place-items: center; /* 简写:align-items justify-items */
justify-content: start; /* 整个网格在容器中的水平对齐 */
align-content: start; /* 整个网格在容器中的垂直对齐 */
place-content: center; /* 简写 */
/* 6. grid-template 简写 */
grid-template:
"header header" 60px
"sidebar content" 1fr
"footer footer" 40px
/ 200px 1fr;
}子元素属性
css
.item {
/* 1. 指定位置 */
grid-column-start: 1;
grid-column-end: 3; /* 跨越第1-2列 */
grid-column: 1 / 3; /* 简写 */
grid-column: 1 / span 2; /* 从第1列开始,跨越2列 */
grid-column: 1 / -1; /* 从第1列到最后一列 */
grid-row-start: 1;
grid-row-end: 4;
grid-row: 1 / 4; /* 跨越第1-3行 */
/* 2. 区域定位 */
grid-area: header; /* 匹配 grid-template-areas 中的名称 */
grid-area: 1 / 1 / 3 / 3; /* row-start / col-start / row-end / col-end */
/* 3. 单元格对齐 */
justify-self: start | end | center | stretch;
align-self: start | end | center | stretch;
place-self: center;
/* 4. 命名网格线定位 */
grid-column: start / end; /* 使用之前命名的网格线 */
}fr 单位与 minmax()
css
/* fr = fraction(可用空间的份数) */
.grid {
/* 可用空间 = 容器宽度 - gap - 固定宽度列 */
grid-template-columns: 200px 1fr 2fr;
/* 第一列200px,剩余空间1:2分配 */
}
/* minmax(min, max) */
.grid {
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
/* 每列最少200px,最大等分剩余空间 */
/* auto-fill: 尽可能多的列(空的也占位) */
/* auto-fit: 尽可能多的列(空的折叠为0) */
}
/* auto-fill vs auto-fit 区别 */
/* 容器 1000px,每项 minmax(200px, 1fr),只有3个子元素 */
/* auto-fill: 创建5列(2个空列占位),每列200px */
/* auto-fit: 创建3列,每列 333px(空列折叠,剩余空间分配给现有列) */Subgrid(子网格,CSS Grid Level 2)
css
.parent {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: auto auto;
gap: 10px;
}
.child {
grid-column: 1 / 4; /* 跨越3列 */
display: grid;
/* subgrid 继承父网格的轨道定义 */
grid-template-columns: subgrid; /* 使用父网格的列轨道 */
grid-template-rows: subgrid; /* 使用父网格的行轨道 */
/* 子元素会对齐到父网格的网格线 */
}
/* 典型应用:卡片列表中标题/内容/按钮对齐 */
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
.card {
display: grid;
grid-template-rows: subgrid; /* 每张卡片的行高与其他卡片对齐 */
grid-row: span 3; /* 每张卡片占3行(标题+内容+按钮) */
}经典布局方案
水平垂直居中
css
/* 方案1:Flex */
.center-flex {
display: flex;
justify-content: center;
align-items: center;
}
/* 方案2:Grid */
.center-grid {
display: grid;
place-items: center;
}
/* 方案3:Grid + margin */
.center-grid-margin {
display: grid;
}
.center-grid-margin > .child {
margin: auto;
}
/* 方案4:position + transform(不需要知道子元素尺寸) */
.center-pos {
position: relative;
}
.center-pos > .child {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
/* 方案5:position + inset + margin(已知或不定尺寸均可) */
.center-inset {
position: relative;
}
.center-inset > .child {
position: absolute;
inset: 0;
margin: auto;
width: fit-content;
height: fit-content;
}圣杯布局(Holy Grail)
css
/* Grid 方案(推荐) */
.holy-grail {
display: grid;
grid-template:
"header header header" 60px
"nav main aside" 1fr
"footer footer footer" 40px
/ 200px 1fr 200px;
min-height: 100vh;
}
.header { grid-area: header; }
.nav { grid-area: nav; }
.main { grid-area: main; }
.aside { grid-area: aside; }
.footer { grid-area: footer; }
/* 响应式:小屏单列 */
@media (max-width: 768px) {
.holy-grail {
grid-template:
"header" 60px
"main" 1fr
"nav" auto
"aside" auto
"footer" 40px
/ 1fr;
}
}瀑布流布局
css
/* CSS columns 方案 */
.masonry-columns {
column-count: 3;
column-gap: 16px;
}
.masonry-columns .item {
break-inside: avoid;
margin-bottom: 16px;
}
/* CSS Grid masonry(实验性,Firefox 支持) */
.masonry-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-template-rows: masonry; /* 实验性 */
gap: 16px;
}等高列
css
/* Flex 自动等高 */
.equal-height {
display: flex;
gap: 20px;
}
.equal-height .column {
flex: 1;
/* align-items 默认 stretch,所有列自动等高 */
}
/* Grid 自动等高 */
.equal-height-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
/* 同一行的所有单元格自动等高 */
}粘性底部(Sticky Footer)
css
/* Flex 方案 */
body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
main {
flex: 1; /* 占据剩余空间 */
}
footer {
/* 自动粘在底部 */
}
/* Grid 方案 */
body {
display: grid;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
}Flex vs Grid 选择指南
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 一维排列(导航栏、工具栏) | Flex | 一维布局更简单 |
| 表单布局 | Grid | 标签和输入框对齐 |
| 卡片列表 | Grid + auto-fill | 自动响应式 |
| 圣杯/双飞翼布局 | Grid | 二维布局直观 |
| 居中 | Grid place-items | 最简洁 |
| 等间距分布 | Flex space-between | 语义清晰 |
| 不规则布局 | Grid | 可命名区域 |
| 内容驱动(大小不确定) | Flex | 自动伸缩 |
| 布局驱动(结构固定) | Grid | 精确控制 |
总结:
• Flex = 一维(行或列),内容优先
• Grid = 二维(行和列),布局优先
• 实际项目中经常混合使用
• Grid 做页面整体布局,Flex 做组件内部布局总结
Flex & Grid 核心知识点:
┌──────────────────────────────────────────────────────────┐
│ Flexbox │
│ • 容器:direction / wrap / justify / align / gap │
│ • 子元素:grow / shrink / basis / order / align-self │
│ • flex: 1 = flex: 1 1 0% │
│ • 计算:剩余空间 = 容器 - Σbasis,按 grow/shrink 分配 │
├──────────────────────────────────────────────────────────┤
│ Grid │
│ • 模板:template-columns/rows / template-areas │
│ • 定位:grid-column/row / grid-area │
│ • 函数:repeat() / minmax() / auto-fill / auto-fit │
│ • fr 单位:可用空间的份数 │
│ • Subgrid:子网格继承父网格轨道 │
├──────────────────────────────────────────────────────────┤
│ 选择原则 │
│ • 一维选 Flex,二维选 Grid │
│ • 内容驱动选 Flex,布局驱动选 Grid │
│ • 混合使用:Grid 做骨架,Flex 做组件 │
└──────────────────────────────────────────────────────────┘