文章 / 前端

Flex 还是 Grid?一份基于场景的取舍清单

刚写 CSS 那几年,我总在两个属性之间犹豫:这个横向排列用 Flex 还是 Grid?后来想明白一件事——判断标准不是"哪个更强",而是你要控制的是一维还是二维。

一句话判断

  • 内容驱动、沿一个方向排队 → Flex
  • 容器驱动、行列同时对齐 → Grid

导航栏、按钮组、表单项、标签流,这些元素的数量和宽度是内容决定的,用 Flex。整页骨架、卡片墙、仪表盘,行列都得对齐,用 Grid。

Flex 的主场

两端对齐的头部

.header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

gap 出现之后,再也不用给子元素写 margin-right 再 :last-child 清除,代码短了一大截。

等宽按钮,且不被文字撑开

.actions > button {
  flex: 1 1 0;      /* 三个数字:放大、缩小、基准宽度 */
  min-width: 0;     /* 关键,见下面 */
}

flex: 1 1 0 的意思是"基准宽度算 0,剩余空间平均分",这样两个按钮无论文字长短都一样宽。如果写 flex: 1,它等价于 flex: 1 1 0%,效果一致;但写 flex: auto 就是 1 1 auto,宽度会被内容带偏。

单行省略号失效的那个坑

这大概是 Flex 最经典的坑:text-overflow: ellipsis 写了,就是不出省略号。

原因在于 flex item 的 min-width 默认是 auto,它拒绝被压缩到内容宽度以下。解决办法就一行:

.row { display: flex; gap: 12px; }
.row .title {
  flex: 1;
  min-width: 0;              /* 允许收缩到内容宽度以下 */
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

同一类问题在 Grid 里也存在,minmax(0, 1fr) 就是它的解法。

Grid 的主场

卡片墙,不写媒体查询

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 20px;
}

这一段的含义是:每列至少 280px,能塞几列塞几列,剩余空间均分。窗口从 1920 缩到 400,布局自然过渡,一行媒体查询都不需要。

auto-fill 和 auto-fit 的区别只在最后一行:容器很宽而卡片很少时,auto-fill 会留下空轨道,auto-fit 会把现有卡片拉宽填满。卡片墙一般用 auto-fill,想让少数几张图铺满整行就用 auto-fit。

整页骨架

.layout {
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr);
  grid-template-rows: 64px 1fr auto;
  grid-template-areas:
    "sidebar topbar"
    "sidebar main"
    "sidebar footer";
  min-height: 100vh;
  gap: 16px;
}
.layout > .sidebar { grid-area: sidebar; }
.layout > .main    { grid-area: main; }

用 grid-template-areas 描述布局,HTML 里直接读得懂结构,改动时改 ASCII 图就行,比一堆百分比定位好维护得多。

什么时候混着用

实际项目里,两种布局是嵌套的,不冲突:

.page {
  display: grid;                                   /* 二维:页面骨架 */
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 20px;
}
.card {
  display: flex;                                   /* 一维:卡片内部 */
  flex-direction: column;
}
.card .tags { display: flex; flex-wrap: wrap; gap: 6px; }
.card .foot { margin-top: auto; }                  /* 底部对齐,卡片等高时很好用 */

外层 Grid 定格子,内层 Flex 排内容。margin-top: auto 在 flex 列里能把页脚顶到底部,是等高卡片对齐底部按钮的最省事写法。

结论

别背属性表,就问自己两个问题:

  1. 我要对齐的是"一条线"还是"一张网格"?
  2. 宽度由内容决定,还是由容器决定?

一条线加内容驱动,Flex。一张网格加容器驱动,Grid。答完这两个问题,选型就没悬念了。