Flexbox + Grid 布局
Flexbox + Grid 布局
简介
CSS 布局经历了从浮动到 Flexbox 再到 Grid 的演进。Flexbox 擅长一维布局(行或列),Grid 擅长二维布局(行和列同时控制)。现代 CSS 布局推荐 Flexbox + Grid 组合使用,Flexbox 处理组件内部布局,Grid 处理页面整体布局。
在传统 CSS 布局中,开发者依赖 float、inline-block 和 table 来实现复杂布局,这些方式往往需要大量的 hack(如 clearfix、负 margin 等)。Flexbox 于 2012 年被 W3C 正式推荐,Grid Layout 则在 2017 年获得主流浏览器支持。如今两者已成为现代 CSS 布局的基石。
布局选择决策树:
- 需要在一维方向(单行或单列)上排列元素? -> Flexbox
- 需要同时在行和列两个维度上控制布局? -> Grid
- 组件内部小元素排列(按钮组、标签、列表项)? -> Flexbox
- 页面级骨架结构(后台布局、仪表盘、杂志排版)? -> Grid
- 需要元素重叠或精确定位? -> Grid(grid-area + z-index)
特点
Flexbox
核心概念
Flexbox 布局模型包含两个角色:Flex 容器(设置 display: flex 的元素)和 Flex 子项(容器的直接子元素)。理解 Flexbox 的关键是掌握两条轴线:
- 主轴(Main Axis):由
flex-direction决定方向,默认为水平从左到右 - 交叉轴(Cross Axis):始终与主轴垂直
主轴 (flex-direction: row)
<------------------------------------>
| item1 | item2 | item3 | item4 |
<------------------------------------>
交叉轴 (垂直方向)弹性布局
/* Flex 容器 */
.flex-container {
display: flex;
flex-direction: row; /* row | column | row-reverse | column-reverse */
justify-content: flex-start; /* 主轴对齐:flex-start | center | space-between | space-around | space-evenly */
align-items: stretch; /* 交叉轴对齐:stretch | flex-start | center | flex-end | baseline */
flex-wrap: nowrap; /* nowrap | wrap | wrap-reverse */
gap: 16px; /* 子元素间距 */
}
/* Flex 子项 */
.flex-item {
flex-grow: 0; /* 放大比例 */
flex-shrink: 1; /* 缩小比例 */
flex-basis: auto; /* 初始大小 */
flex: 1; /* 简写:flex-grow: 1, flex-shrink: 1, flex-basis: 0% */
align-self: center; /* 单独对齐 */
order: 0; /* 排列顺序 */
}
/* 常见布局模式 */
/* 1. 水平垂直居中 */
.center {
display: flex;
justify-content: center;
align-items: center;
}
/* 2. 导航栏 */
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 24px;
height: 60px;
}
/* 3. 等分布局 */
.equal-columns {
display: flex;
gap: 20px;
}
.equal-columns > * { flex: 1; }
/* 4. 侧边栏 + 主内容 */
.sidebar-layout {
display: flex;
min-height: 100vh;
}
.sidebar { width: 240px; flex-shrink: 0; }
.main-content { flex: 1; }
/* 5. 底部固定 */
.sticky-footer {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.sticky-footer .content { flex: 1; }flex 简写的深入理解
flex 属性是 flex-grow、flex-shrink、flex-basis 的简写,常见值有特殊含义:
/* flex 的常见写法解析 */
.item { flex: 0 1 auto; } /* 默认值:不放大,可缩小,初始大小由内容决定 */
.item { flex: 1; } /* 等价于 flex: 1 1 0% —— 所有等分元素平分剩余空间 */
.item { flex: auto; } /* 等价于 flex: 1 1 auto —— 按内容大小比例分配 */
.item { flex: none; } /* 等价于 flex: 0 0 auto —— 固定大小,不伸缩 */
.item { flex: 0 0 200px; } /* 固定 200px,不放大不缩小 */
.item { flex: 1 1 300px; } /* 最小 300px,多余空间按比例分配 */
/* 生产中常见的等分 vs 按内容比例分配的区别 */
.equal-split > * {
flex: 1; /* 所有子项严格等分,忽略内容差异 */
}
.content-based > * {
flex: auto; /* 按内容比例分配空间 */
}实战模式:等高列
Flexbox 天然支持等高列,这是相比传统 float 布局的一个重大优势:
/* 等高卡片布局 —— 无需任何 hack */
.card-row {
display: flex;
gap: 24px;
}
.card-row .card {
flex: 1;
/* 无论每个卡片内容多少,高度自动相等 */
padding: 24px;
border-radius: 8px;
background: #fff;
}
/* 实际项目中的产品卡片示例 */
.product-list {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.product-card {
flex: 1 1 280px; /* 最小宽度 280px,多余空间等分 */
max-width: calc(33.333% - 14px); /* 最多一行 3 个 */
display: flex;
flex-direction: column;
}
.product-card .card-body { flex: 1; } /* 内容区域撑满 */
.product-card .card-footer {
flex-shrink: 0;
border-top: 1px solid #eee;
padding-top: 16px;
}实战模式:安全区域导航栏
/* 兼容刘海屏的安全区域导航栏 */
.safe-navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 24px;
padding-top: env(safe-area-inset-top);
height: calc(60px + env(safe-area-inset-top));
background: #1a1a2e;
color: #fff;
}
/* 移动端底部 Tab 栏 */
.bottom-tabs {
display: flex;
justify-content: space-around;
align-items: center;
padding-bottom: env(safe-area-inset-bottom);
height: calc(56px + env(safe-area-inset-bottom));
background: #fff;
position: fixed;
bottom: 0;
left: 0;
right: 0;
}flex-wrap 与响应式
/* 响应式 Flex 布局 —— 不依赖媒体查询 */
.responsive-list {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.responsive-list > * {
flex: 1 1 300px; /* 每项最小 300px,自动换行 */
}
/* 更精细的控制:配合 min-width 实现断点效果 */
.auto-grid {
display: flex;
flex-wrap: wrap;
margin: -8px; /* 抵消内部 gap 的边缘 */
}
.auto-grid > * {
flex: 0 0 calc(25% - 16px); /* 默认一行 4 个 */
margin: 8px;
}
@media (max-width: 1024px) {
.auto-grid > * { flex: 0 0 calc(33.333% - 16px); } /* 一行 3 个 */
}
@media (max-width: 768px) {
.auto-grid > * { flex: 0 0 calc(50% - 16px); } /* 一行 2 个 */
}
@media (max-width: 480px) {
.auto-grid > * { flex: 0 0 100%; margin-left: 8px; margin-right: 8px; } /* 单列 */
}align-items 各值的区别
/* align-items 的 5 个取值对比 */
.demo-container {
display: flex;
height: 200px;
align-items: stretch; /* 默认值:子项拉伸到容器高度 */
/* align-items: flex-start; 顶部对齐 */
/* align-items: flex-end; 底部对齐 */
/* align-items: center; 垂直居中 */
/* align-items: baseline; 文本基线对齐(不同字号时有用) */
}
/* baseline 对齐的实际应用 —— 混合字号行 */
.tag-row {
display: flex;
align-items: baseline;
gap: 8px;
}
.tag-row .tag-lg { font-size: 24px; }
.tag-row .tag-sm { font-size: 12px; }
/* 所有 tag 的文本基线对齐,视觉上更整齐 */gap 属性详解
/* gap 是 row-gap 和 column-gap 的简写 */
.container {
gap: 16px; /* 行间距 16px,列间距 16px */
gap: 16px 24px; /* 行间距 16px,列间距 24px */
row-gap: 16px;
column-gap: 24px;
}
/* gap 不会作用于容器边缘 */
/* 相当于传统 margin 方案的 margin-top + 伪元素:first-child 去除首项间距 */
.flex-gap-demo {
display: flex;
gap: 12px;
}
/* 3 个子项,总共 2 个间距,每个 12px */
/* 子项 1 [12px] 子项 2 [12px] 子项 3 */Grid
核心概念
CSS Grid 是一个二维布局系统,可以同时控制行和列。与 Flexbox 不同,Grid 在容器层面定义整体轨道(track),然后让子项放入对应的网格区域。
Grid 布局的核心术语:
- 轨道(Track):行或列,由
grid-template-columns/rows定义 - 单元格(Cell):行和列交叉形成的最小区域
- 网格线(Grid Line):划分轨道的线,从 1 开始编号
- 网格区域(Grid Area):由矩形网格线围成的区域
- 间距(Gap):轨道之间的间隔
网格布局
/* Grid 容器 */
.grid-container {
display: grid;
grid-template-columns: repeat(3, 1fr); /* 3 等分列 */
grid-template-columns: 240px 1fr 240px; /* 固定-弹性-固定 */
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); /* 响应式网格 */
grid-template-rows: auto 1fr auto; /* 3 行 */
gap: 20px;
grid-auto-rows: minmax(200px, auto); /* 隐式行 */
}
/* 常见布局模式 */
/* 1. 经典后台布局 */
.admin-layout {
display: grid;
grid-template-areas:
"header header header"
"sidebar content aside"
"footer footer footer";
grid-template-columns: 240px 1fr 200px;
grid-template-rows: 60px 1fr 40px;
min-height: 100vh;
}
.admin-layout .header { grid-area: header; }
.admin-layout .sidebar { grid-area: sidebar; }
.admin-layout .content { grid-area: content; }
.admin-layout .aside { grid-area: aside; }
.admin-layout .footer { grid-area: footer; }
/* 2. 响应式卡片网格 */
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 24px;
}
/* 3. 12 列栅格系统 */
.grid-12 {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 16px;
}
.col-6 { grid-column: span 6; } /* 占一半 */
.col-4 { grid-column: span 4; } /* 占 1/3 */
.col-3 { grid-column: span 3; } /* 占 1/4 */
.col-8 { grid-column: span 8; }
/* 4. 圣杯布局 */
.holy-grail {
display: grid;
grid-template:
"header header header" 60px
"nav main aside" 1fr
"footer footer footer" 40px / 200px 1fr 200px;
}
/* 5. 响应式断点 */
@media (max-width: 768px) {
.admin-layout {
grid-template-areas:
"header"
"content"
"footer";
grid-template-columns: 1fr;
grid-template-rows: 60px 1fr 40px;
}
.sidebar { display: none; }
}Grid 轨道函数详解
/* fr 单位 —— 比例分配剩余空间 */
.grid-fr {
grid-template-columns: 1fr 2fr 1fr;
/* 总剩余空间分 4 份:1份 2份 1份 */
/* 等价于 25% 50% 25%(在无固定宽度时) */
}
/* repeat() —— 重复轨道定义 */
.grid-repeat {
grid-template-columns: repeat(4, 1fr); /* 4 等分 */
grid-template-columns: repeat(3, 100px 1fr); /* 重复 3 次:100px 1fr 100px 1fr 100px 1fr */
}
/* minmax() —— 设置轨道最小和最大尺寸 */
.grid-minmax {
grid-template-columns: minmax(200px, 1fr);
/* 最小 200px,最大平分剩余空间 */
}
/* auto-fill vs auto-fit 的区别 */
.grid-auto-fill {
/* auto-fill:尽可能多地创建列,空列保留空间 */
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
.grid-auto-fit {
/* auto-fit:尽可能多地创建列,空列折叠为 0 */
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
/* fit-content() —— 内容自适应但不超过最大值 */
.grid-fit-content {
grid-template-columns: fit-content(300px) 1fr;
/* 第一列宽度由内容决定,但最大不超过 300px */
}auto-fill 与 auto-fit 的关键区别
/* 假设容器宽度 1000px,minmax(200px, 1fr) */
/* auto-fill: 创建尽可能多的 200px 列 */
/* 1000px / 200px = 5 列,每列实际 200px */
.container-fill {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
/* 如果只有 2 个子项:[item1] [item2] [空] [空] [空] */
/* 空列仍占据空间,子项保持 200px */
}
/* auto-fit: 创建尽可能多的列,空列折叠 */
/* 1000px / 200px = 5 列,但只有 2 个子项时折叠为 2 列 */
.container-fit {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
/* 如果只有 2 个子项:[item1 ---- 500px ----] [item2 ---- 500px ----] */
/* 空列折叠,子项拉伸填满 */
}
/* 实际选择建议:
- 卡片网格,子项数量动态:用 auto-fit(让卡片自动拉宽)
- 需要固定列数模板:用 auto-fill(保持列宽一致)
*/Grid 网格线定位
/* 通过网格线编号定位子项 */
.grid-line-demo {
display: grid;
grid-template-columns: 100px 200px 100px;
grid-template-rows: 80px 120px;
/* 网格线编号:|1| 100px |2| 200px |3| 100px |4| */
}
/* 子项占据从第 2 条到第 4 条列线(即第 2、3 列) */
.span-item {
grid-column: 2 / 4; /* 从线 2 到线 4 */
grid-row: 1 / 3; /* 从线 1 到线 3 */
}
/* 使用 span 关键字 */
.span-shortcut {
grid-column: 2 / span 2; /* 从线 2 开始,跨 2 列 */
grid-row: span 2; /* 跨 2 行(从当前自动位置开始) */
}
/* 使用 -1 从末尾计数 */
.from-end {
grid-column: 2 / -1; /* 从线 2 到最后一条线 */
}Grid 命名区域深入
/* grid-template-areas 的进阶用法 */
.dashboard {
display: grid;
grid-template-areas:
"header header header header"
"sidebar main main aside"
"sidebar main main aside"
"footer footer footer footer";
grid-template-columns: 220px 1fr 1fr 200px;
grid-template-rows: 64px 1fr 1fr 48px;
min-height: 100vh;
}
/* 区域可以不连续 —— 但不推荐,会增加复杂度 */
/* 每个命名区域必须是矩形 */
/* 使用点号 . 表示空区域 */
.sparse-grid {
display: grid;
grid-template-areas:
"header header header"
". main ."
"footer footer footer";
grid-template-columns: 100px 1fr 100px;
}
/* 响应式区域切换 */
@media (max-width: 768px) {
.dashboard {
grid-template-areas:
"header"
"main"
"sidebar"
"aside"
"footer";
grid-template-columns: 1fr;
grid-template-rows: 64px 1fr auto auto 48px;
}
}Grid 子项对齐
/* justify-items / align-items —— 控制所有子项在其网格区域内的对齐 */
.grid-align {
display: grid;
justify-items: start; /* 水平:start | center | end | stretch(默认) */
align-items: start; /* 垂直:start | center | end | stretch(默认) */
place-items: center; /* 简写:align-items + justify-items */
}
/* justify-content / align-content —— 控制整个网格在容器内的对齐 */
.grid-content-align {
display: grid;
grid-template-columns: repeat(3, 100px);
justify-content: center; /* 网格整体水平居中 */
align-content: center; /* 网格整体垂直居中 */
place-content: center; /* 简写 */
}
/* 单个子项对齐 */
.grid-item-self {
justify-self: end; /* 该子项水平靠右 */
align-self: end; /* 该子项垂直靠底 */
place-self: end; /* 简写 */
}实战:仪表盘布局
/* 数据仪表盘 —— Grid 的典型应用场景 */
.dashboard-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: auto repeat(2, 200px) auto;
gap: 20px;
padding: 20px;
}
/* 头部统计卡片 */
.stat-card {
grid-column: span 1;
}
/* 大图表占据两列两行 */
.main-chart {
grid-column: span 2;
grid-row: span 2;
}
/* 侧边列表 */
.side-list {
grid-column: span 1;
grid-row: span 2;
}
/* 底部表格 */
.data-table {
grid-column: 1 / -1; /* 横跨所有列 */
}
/* 响应式 */
@media (max-width: 1024px) {
.dashboard-grid {
grid-template-columns: repeat(2, 1fr);
}
.main-chart {
grid-column: span 2;
grid-row: span 1;
}
.side-list {
grid-column: span 2;
grid-row: span 1;
}
}
@media (max-width: 640px) {
.dashboard-grid {
grid-template-columns: 1fr;
}
.stat-card,
.main-chart,
.side-list,
.data-table {
grid-column: 1 / -1;
grid-row: auto;
}
}Grid 自动布局与 dense
/* grid-auto-flow 控制自动放置算法 */
.auto-flow-row {
display: grid;
grid-auto-flow: row; /* 默认:先填行再换行 */
grid-auto-flow: column; /* 先填列再换列 */
grid-auto-flow: dense; /* 紧密填充:自动回填空位 */
grid-auto-flow: row dense; /* 行优先 + 紧密填充 */
}
/* dense 的实际效果 */
.masonry-like {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-flow: dense;
gap: 16px;
}
.masonry-like .tall { grid-row: span 2; }
.masonry-like .wide { grid-column: span 2; }
/* dense 模式下,小元素会自动填充大元素留下的空位 */
/* 注意:dense 可能导致 DOM 顺序与视觉顺序不一致,影响无障碍 */
/* 仅在视觉顺序不重要时使用(如图片墙) */Grid 隐式网格
/* 当子项超出显式定义的行列数时,自动创建隐式网格 */
.implicit-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: 100px 200px;
/* 显式定义了 3 列 2 行 */
/* 第 6 个及以后的子项将进入隐式网格 */
grid-auto-rows: minmax(100px, auto); /* 隐式行的高度 */
grid-auto-columns: 200px; /* 隐式列的宽度(默认由内容决定) */
}
/* grid-auto-flow: column 时隐式列生效 */
.column-flow {
display: grid;
grid-template-rows: repeat(3, 80px);
grid-auto-flow: column;
grid-auto-columns: 150px;
gap: 10px;
}
/* 子项先向下填充 3 行,再创建新的隐式列 */对齐系统
Place 对齐
/* 简写属性 */
place-items: center; /* align-items + justify-items */
place-content: center; /* align-content + justify-content */
place-self: center; /* align-self + justify-self */
/* 常用对齐组合 */
/* 水平垂直居中 */
.perfect-center {
display: grid;
place-items: center;
}
/* 均匀分布 */
.spread-evenly {
display: flex;
place-content: center space-evenly;
}对齐属性完整对照表
| 属性 | 作用范围 | 适用布局 |
|---|---|---|
justify-content | 主轴方向,控制子项之间的间距 | Flex |
align-items | 交叉轴方向,控制子项在轴线上的对齐 | Flex |
align-content | 多行/多列时,控制行/列之间的间距 | Flex(需 wrap) |
justify-items | 水平方向,控制子项在其网格区域内的对齐 | Grid |
align-items | 垂直方向,控制子项在其网格区域内的对齐 | Grid |
justify-content | 水平方向,控制整个网格在容器内的对齐 | Grid |
align-content | 垂直方向,控制整个网格在容器内的对齐 | Grid |
justify-self | 单个 Grid 子项水平对齐 | Grid |
align-self | 单个 Flex/Grid 子项交叉轴对齐 | Flex + Grid |
place-self | 单个 Grid 子项双向对齐 | Grid |
margin: auto 在 Flexbox/Grid 中的特殊行为
/* 在 Flexbox 中,margin: auto 会吸收剩余空间 */
.flex-auto-margin {
display: flex;
}
.flex-auto-margin .left { margin-right: auto; }
/* .left 右侧的剩余空间全部被 margin: auto 占据 */
/* 效果等价于 justify-content: space-between(但更灵活) */
/* 另一个例子:单个子项居中 */
.flex-single-center {
display: flex;
}
.flex-single-center > * {
margin: auto; /* 水平垂直都居中 */
}
/* Grid 中 margin: auto 也有类似效果 */
.grid-auto-margin {
display: grid;
grid-template-columns: 1fr 1fr;
}
.grid-auto-margin .item {
justify-self: center; /* 或 margin: 0 auto */
align-self: center; /* 或 margin: auto 0 */
}Flexbox 与 Grid 的选择策略
何时使用 Flexbox
/* 1. 导航栏 —— 一维水平排列 */
nav {
display: flex;
align-items: center;
gap: 16px;
}
/* 2. 卡片内部结构 —— 标题 + 描述 + 按钮 */
.card {
display: flex;
flex-direction: column;
gap: 12px;
}
.card .actions {
display: flex;
justify-content: flex-end;
gap: 8px;
margin-top: auto; /* 按钮推到底部 */
}
/* 3. 表单行 —— 标签 + 输入框 + 错误信息 */
.form-row {
display: flex;
align-items: center;
gap: 12px;
}
.form-row label { flex-shrink: 0; width: 100px; }
.form-row input { flex: 1; }
/* 4. 标签/徽章组 */
.tag-group {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
/* 5. 加载状态 —— spinner + 文字 */
.loading {
display: flex;
align-items: center;
gap: 8px;
}何时使用 Grid
/* 1. 后台管理系统整体布局 */
.admin {
display: grid;
grid-template-areas:
"aside header"
"aside main";
grid-template-columns: 240px 1fr;
grid-template-rows: 64px 1fr;
height: 100vh;
}
/* 2. 仪表盘多面板 */
.dashboard {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: minmax(180px, auto);
gap: 16px;
}
/* 3. 图片/相册网格 */
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 8px;
}
/* 4. 表单网格布局(双列表单) */
.form-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px 24px;
}
.form-grid .full-width {
grid-column: 1 / -1; /* 跨两列 */
}
/* 5. 复杂杂志/博客布局 */
.magazine {
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-template-rows: auto;
gap: 24px;
}
.magazine .featured { grid-column: 1 / 8; grid-row: span 2; }
.magazine .sidebar-top { grid-column: 8 / 13; }
.magazine .sidebar-bottom { grid-column: 8 / 13; }
.magazine .article { grid-column: span 4; }Flexbox 与 Grid 混合使用
/* 最佳实践:外层 Grid 定骨架,内层 Flex 排组件 */
.page-layout {
display: grid;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
grid-template-columns: 260px 1fr;
grid-template-rows: 64px 1fr 48px;
min-height: 100vh;
}
/* Grid 的 header 内部用 Flex 排列导航项 */
.page-header {
grid-area: header;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 24px;
}
/* Grid 的 main 内部用 Flex 排列工具栏 */
.page-main {
grid-area: main;
display: flex;
flex-direction: column;
padding: 24px;
}
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
}
.content-area { flex: 1; }响应式布局最佳实践
Container Queries(容器查询)
/* 容器查询 —— 基于父容器宽度而非视口宽度 */
.card-container {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 400px) {
.card {
display: flex;
gap: 16px;
}
.card .card-image {
width: 150px;
flex-shrink: 0;
}
}
@container card (max-width: 399px) {
.card {
display: flex;
flex-direction: column;
}
.card .card-image {
width: 100%;
}
}
/* 容器查询比媒体查询更适合组件库开发 */
/* 因为组件不知道自己会被放在页面的什么位置 */clamp() 流式排版
/* clamp(最小值, 首选值, 最大值) */
/* 避免硬断点,实现平滑缩放 */
h1 {
font-size: clamp(1.5rem, 4vw, 3rem);
/* 最小 1.5rem,最大 3rem,中间按视口宽度线性缩放 */
}
.container {
width: clamp(320px, 90%, 1200px);
margin: 0 auto;
/* 最小 320px,最大 1200px,中间 90% 视口宽度 */
padding: 0 clamp(16px, 4vw, 48px);
}
/* 配合 Grid 使用流式布局 */
.fluid-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
gap: clamp(12px, 2vw, 24px);
padding: clamp(16px, 3vw, 48px);
}逻辑属性
/* 传统物理属性 vs 逻辑属性(支持 RTL/LTR 切换) */
/* 传统写法 */
.box {
margin-left: 16px;
padding-right: 8px;
border-right: 1px solid #ddd;
text-align: left;
}
/* 逻辑属性写法 —— 自动适配书写方向 */
.box {
margin-inline-start: 16px; /* 在 LTR 中 = margin-left,RTL 中 = margin-right */
padding-inline-end: 8px; /* 在 LTR 中 = padding-right */
border-inline-end: 1px solid #ddd;
text-align: start; /* 在 LTR 中 = left */
}
/* Flexbox 中的逻辑方向 */
.flex-logical {
display: flex;
flex-direction: row; /* 主轴跟随内联方向(LTR 从左到右,RTL 从右到左) */
justify-content: flex-start; /* flex-start 自动适配书写方向 */
gap: 16px;
}
/* Grid 中的逻辑属性 */
.grid-logical {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-rows: auto 1fr;
gap: 16px;
padding-block: 24px; /* 上下 padding */
padding-inline: 32px; /* LTR 中左右 padding */
}性能优化与生产注意事项
避免常见的布局性能陷阱
/* 1. 避免使用百分比 padding-bottom 实现等比容器(旧方案) */
/* 旧方案:需要额外的定位 hack */
.aspect-old {
position: relative;
padding-bottom: 56.25%; /* 16:9 */
}
.aspect-old > * {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
/* 新方案:使用 aspect-ratio */
.aspect-new {
aspect-ratio: 16 / 9;
width: 100%;
}
/* 2. 避免嵌套过深的 Flex/Grid 容器 */
/* 每层嵌套都会增加布局计算成本 */
/* 建议:最多 3-4 层嵌套 */
/* 3. 固定尺寸子项使用 flex: none 而非 flex: 0 */
.fixed-item {
flex: none; /* 明确表示不参与伸缩 */
width: 200px;
}
/* 4. content-visibility 优化大列表渲染 */
.offscreen-section {
content-visibility: auto;
contain-intrinsic-size: 0 500px; /* 预估高度,减少回流 */
}与 CSS 变量结合
/* 使用 CSS 变量让布局更灵活可配置 */
:root {
--sidebar-width: 240px;
--header-height: 64px;
--footer-height: 48px;
--grid-gap: 20px;
--card-min-width: 280px;
}
.layout {
display: grid;
grid-template-columns: var(--sidebar-width) 1fr;
grid-template-rows: var(--header-height) 1fr var(--footer-height);
gap: var(--grid-gap);
min-height: 100vh;
}
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(var(--card-min-width), 1fr));
gap: var(--grid-gap);
}
/* 通过 JS 动态切换主题 */
/* document.documentElement.style.setProperty('--sidebar-width', '200px'); */
/* 折叠侧边栏 */
.layout.collapsed {
--sidebar-width: 64px;
}在 Vue/React 中使用
<!-- Vue 3 组合式 API + Flex/Grid -->
<template>
<div class="page-layout">
<header class="page-header">
<div class="logo">MyApp</div>
<nav class="nav-links">
<a v-for="item in navItems" :key="item.path" :href="item.path">
{{ item.label }}
</a>
</nav>
<div class="user-info">
<span>{{ username }}</span>
</div>
</header>
<aside class="sidebar" :class="{ collapsed: sidebarCollapsed }">
<slot name="sidebar" />
</aside>
<main class="main-content">
<slot />
</main>
</div>
</template>
<style scoped>
.page-layout {
display: grid;
grid-template-areas:
"header header"
"sidebar main";
grid-template-columns: var(--sidebar-width, 240px) 1fr;
grid-template-rows: 64px 1fr;
min-height: 100vh;
transition: grid-template-columns 0.3s ease;
}
.page-layout.collapsed {
grid-template-columns: 64px 1fr;
}
.page-header {
grid-area: header;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 24px;
}
.sidebar {
grid-area: sidebar;
overflow-y: auto;
}
.main-content {
grid-area: main;
padding: 24px;
overflow-y: auto;
}
</style>// React 中的仪表盘 Grid 布局
function Dashboard() {
const stats = [
{ title: '用户总数', value: '12,345', color: '#4CAF50' },
{ title: '今日活跃', value: '1,234', color: '#2196F3' },
{ title: '订单量', value: '567', color: '#FF9800' },
{ title: '收入', value: '¥89,012', color: '#9C27B0' },
];
return (
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 280px), 1fr))',
gap: '20px',
padding: '24px',
}}>
{stats.map((stat) => (
<div
key={stat.title}
style={{
padding: '24px',
borderRadius: '8px',
background: '#fff',
borderLeft: `4px solid ${stat.color}`,
}}
>
<div style={{ color: '#666', fontSize: '14px' }}>{stat.title}</div>
<div style={{ fontSize: '28px', fontWeight: 'bold', marginTop: '8px' }}>
{stat.value}
</div>
</div>
))}
</div>
);
}优点
缺点
总结
CSS 布局选择:一维排列(导航栏、卡片列表、居中)用 Flexbox,二维网格(后台布局、仪表盘、相册)用 Grid。Flexbox 核心:display: flex + justify-content + align-items。Grid 核心:grid-template-columns/rows + grid-template-areas。响应式用 auto-fill + minmax 自动适配。
记忆口诀:
- 组件内部找 Flex(按钮组、表单行、标签列表)
- 页面骨架找 Grid(后台布局、仪表盘、相册墙)
- 响应式用 minmax + auto-fill/fit
- 居中用 flex center 或 grid place-items center
关键知识点
- 先判断主题更偏浏览器原理、框架机制、工程化还是性能优化。
- 前端问题很多看似是页面问题,实际源头在构建、缓存、状态流或接口协作。
- 真正成熟的前端方案一定同时考虑首屏、交互、可维护性和线上诊断。
- 前端主题最好同时看浏览器原理、框架机制和工程化约束。
项目落地视角
- 把组件边界、状态归属、网络层规范和错误处理先定下来。
- 上线前检查包体积、缓存命中、接口失败路径和关键交互降级策略。
- 如果主题和性能有关,最好用 DevTools、Lighthouse 或埋点验证。
- 对关键页面先建立状态流和数据流,再考虑组件拆分。
常见误区
- 只盯框架 API,不理解浏览器和运行时成本。
- 把状态、请求和 UI 更新混成一层,后期难维护。
- 线上问题出现时没有日志、埋点和性能基线可对照。
- 只追框架新特性,不分析实际渲染成本。
进阶路线
- 继续补齐 SSR、边缘渲染、设计系统和监控告警能力。
- 把主题和后端接口约定、CI/CD、缓存策略一起思考。
- 沉淀组件规范、页面模板和性能基线,减少团队差异。
- 继续补齐设计系统、SSR/边缘渲染、监控告警和组件库治理。
适用场景
- 当你准备把《Flexbox + Grid 布局》真正落到项目里时,最适合先在一个独立模块或最小样例里验证关键路径。
- 适合中后台应用、门户站点、组件库和实时交互页面。
- 当需求涉及状态流、路由、网络缓存、SSR/CSR 或性能治理时,这类主题很关键。
落地建议
- 先定义组件边界和状态归属,再落地 UI 细节。
- 对核心页面做首屏、体积、缓存和错误路径检查。
- 把安全、兼容性和可访问性纳入默认交付标准。
排错清单
- 先用浏览器 DevTools 看请求、性能面板和控制台错误。
- 检查依赖版本、构建配置、环境变量和静态资源路径。
- 如果是线上问题,优先确认缓存、CDN 和构建产物是否一致。
复盘问题
- 如果把《Flexbox + Grid 布局》放进你的当前项目,最先要验证的输入、输出和失败路径分别是什么?
- 《Flexbox + Grid 布局》最容易在什么规模、什么边界条件下暴露问题?你会用什么指标或日志去确认?
- 相比默认实现或替代方案,采用《Flexbox + Grid 布局》最大的收益和代价分别是什么?
