CSS
Flexbox 还是 Grid:一个够用的判断方法
不用背属性表。先问尺寸是由内容决定还是由版面决定,再避开几个常见的坑,布局问题就解决了大半。
一个问题就够了
选 Flexbox 还是 Grid,常见的说法是“一维用 Flex,二维用 Grid”。这句话没错,但实际写页面时,我更常问的是另一个问题:尺寸是由内容决定的,还是由版面决定的?
- 内容决定尺寸:元素有多宽就占多宽,排不下再换行或收缩。用 Flexbox。
- 版面决定尺寸:先划好轨道,元素放进去对齐。用 Grid。
| 场景 | 选择 | 原因 |
|---|---|---|
| 导航栏、按钮组、标签列表 | Flexbox | 每一项的宽度取决于文字长度 |
| 卡片网格 | Grid | 行和列都需要对齐 |
| 页面整体框架(侧栏 + 内容) | Grid | 轨道是提前规划好的 |
| 左边图标、右边文字 | Flexbox | 一行内的对齐和分配剩余空间 |
两者也不是二选一。常见做法是外层用 Grid 划分版面,组件内部用 Flexbox 排列内容。
动手试一下 Flexbox
调整下面的选项,观察四个方块的变化。下方的代码会实时更新:
这个互动示例需要启用 JavaScript。
三个常见的 Flexbox 问题
长文本把容器撑破了。 Flex 子项的 min-width 默认是 auto,也就是不能比内容更窄。如果子项里有不换行的长文本或代码,它就不肯收缩。给子项加上 min-width: 0 即可:
.row { display: flex; gap: 12px; }
.row .title {
flex: 1;
min-width: 0; /* 允许收缩到比内容更窄 */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}**flex: 1 到底是什么意思。** 它是 flex-grow: 1; flex-shrink: 1; flex-basis: 0% 的简写。基准宽度为 0,所以几个 flex: 1 的子项会等分空间,和内容多少无关。如果希望先按内容占位,再分配剩余空间,要用 flex: 1 1 auto。
把最后一个元素推到底部或最右边。 不需要额外包一层,在 Flex 容器里,margin-left: auto 或 margin-top: auto 会吃掉所有剩余空间:
.card { display: flex; flex-direction: column; }
.card .action { margin-top: auto; } /* 按钮始终贴底 */不写媒体查询的响应式网格
Grid 最实用的一行代码,是让列数随容器宽度自动变化:
.cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(min(240px, 100%), 1fr));
gap: 16px;
}这里有两个细节:
- **
auto-fill和auto-fit的区别**:只有在元素少、一行放不满时才看得出来。auto-fill会保留空轨道,元素保持原有宽度;auto-fit会折叠空轨道,元素被拉宽填满整行。 - **
min(240px, 100%)** 防止容器比 240px 还窄时溢出。
用 subgrid 对齐卡片内部
一排卡片的标题长短不一,下面的内容就对不齐,这是个老问题。subgrid 让卡片内部直接使用外层网格的行轨道,标题、正文和按钮在所有卡片之间自动对齐:
.cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}
.card {
display: grid;
grid-row: span 3; /* 占三行:标题、正文、按钮 */
grid-template-rows: subgrid;
}小结
先问尺寸由谁决定。内容决定用 Flexbox,版面决定用 Grid,外层 Grid 配内层 Flexbox 是最常见的组合。遇到 Flex 子项撑破容器,第一反应应该是检查 min-width: 0。