CSS Grid布局的fr单位与剩余空间分配算法

750 阅读5分钟

在现代 CSS 布局体系中,CSS Grid 可以说是网页布局的一场革命。而其中最为迷人(也最容易误解)的,就是 fr 单位了。很多开发者初学时以为 1fr + 2fr 就是按比例瓜分空间,看似简单,实则暗藏玄机

今天我们就来剖析这个“玄学单位”——fr,以及它背后的剩余空间分配算法


什么是 fr

frfraction(分数) 的缩写,用于表示 可用剩余空间的比例单位。比如:

grid-template-columns: 1fr 2fr;

意味着:第一个列占用 1 份,第二列占用 2 份 —— 但这只是前提,不是全局的真理


剩余空间?什么叫“剩余”?

这是最容易忽略的一个点:fr 只分配“剩余空间”,那么到底什么是剩余空间呢?

先减去以下这些:

  • 所有固定宽度(如 100px
  • 所有内容撑开的最小宽度(如 min-contentauto
  • 所有 minmax() 中的最小值限制
  • 所有 gap 间隙
  • 滚动条等边界间距

剩下的,才是 fr 可以瓜分的空间。

关键句:fr 单位分的是剩下的蛋糕,不是整个蛋糕。


示例:基础分配

grid-template-columns: 1fr 2fr;

假设容器宽度为 900px,没有 gap,也没有固定宽度或内容最小限制。

那么:

  • 总 fr 份数 = 1 + 2 = 3
  • 每份宽度 = 900px / 3 = 300px
  • 第一列宽度 = 1 × 300px = 300px
  • 第二列宽度 = 2 × 300px = 600px

示例:混合单位陷阱

grid-template-columns: 200px 1fr 2fr;

容器宽度:1000px

  • 第 1 列占 200px(固定)
  • 剩余空间 = 1000px - 200px = 800px
  • fr 总数 = 1 + 2 = 3
  • 每 fr 宽度 = 800px / 3 ≈ 266.66px

计算结果:

  • 第 1 列:200px
  • 第 2 列:1fr = 266.66px
  • 第 3 列:2fr = 533.33px

autofr 的冲突逻辑

现在看看下面这个例子:

grid-template-columns: auto 1fr;

这个布局中,如果第一列内容很多,第一列会先“吃掉”所需空间。剩下的才分配给 1fr

换句话说:

  • auto 会测量内容最小宽度
  • 剩余空间才给 fr

这也是很多人疑惑“为什么 1fr 看起来比 auto 还小”的原因。


min-contentfr 的故事

min-content 是个内容撑开的最小宽度值,在 grid 中它会优先保留。

grid-template-columns: min-content 1fr;

假设第 1 列内容为 “Supercalifragilisticexpialidocious”,它会尽可能展示不换行的长度。

你可以理解为:min-content 是先吃肉的亲戚,fr 是最后打扫餐桌的晚辈。


minmax() 中的 fr

经典用法:

grid-template-columns: minmax(100px, 1fr) 2fr;

这是很多人误解的地方。

规则是:

  1. minmax()1fr 只在 100px 之后才起作用;
  2. 如果总空间不够,minmax(100px, 1fr) 至少会保留 100px;
  3. 一旦空间足够大,1fr 就开始分配剩余部分。

这是一个“有最低生活保障”的 fr


核心算法解析(W3C标准)

根据 W3C CSS Grid Layout Module 文档,fr 单位的算法大致如下:

  1. 收集所有非 fr 项的固定值,包括 px%automin-contentmax-content
  2. 减去这些非 fr 项所占的空间,得到“剩余空间”;
  3. 遍历所有 fr 单位,计算总 fr 权重;
  4. 将剩余空间按比例分配到每个 fr 单元中;
  5. 如果某列设置了 minmax(min, 1fr),并且 1fr 得到的宽度小于 min,则 强制分配最小值,其余 fr 重新计算;
  6. 如果某列设置了最大值,比如 max(200px, 1fr),并且 1fr 太大了超过了上限,也会触发回退机制

这是一个**“先定再分,再回滚修正”**的动态系统。


实际案例:3列响应式布局

.container {
  display: grid;
  grid-template-columns: 1fr 3fr 1fr;
  gap: 20px;
}

你会看到中间一列始终比两侧大,而当屏幕缩小时:

  • fr 会自动重新分配
  • 不需要媒体查询
  • 保证了比例关系

这是 fr 的魅力:比 flex 更适合面向网格的场景。


冷知识:为什么 1fr100% 不一样?

这是一个常见误区。

width: 100%;

意味着这个元素要占满容器宽度,不考虑兄弟元素

而:

grid-template-columns: 1fr 1fr;

意味着在兄弟元素参与下公平分配。即使你只有一个元素,它也不会自动理解为“满宽”,而是等比参与。


常见误解与反直觉行为

  1. 误以为 fr = flex-grow
    实际上 fr 不是 flex-grow。它基于网格整列空间,而不是内容动态膨胀。
  2. 误以为 fr 是绝对宽度的等价表达
    不是。它是“相对剩余空间”的比例表达。
  3. fr 不考虑内容大小?
    错。内容超过 fr 分配的空间时,会发生溢出或被压缩。

小结:理解 fr 的三条金律

  • fr 只分剩余空间,剩下的才论斤卖。
  • 内容相关单位(auto、min-content)优先保留空间。
  • minmax() 是 fr 的保险杠,可以设置最低限度。

最后:fr 值得深入学习吗?

当然值得。

相比于 flex 的“线性流动”布局,Grid + fr 提供了更结构化的二维控制力。尤其在构建复杂后台管理系统、数据面板、响应式报表时,fr 是你不可或缺的帮手。


结尾一问

如果有一天你发现你设置了 grid-template-columns: 1fr 2fr,结果显示却是反的 —— 别急,多半不是 CSS 出错,而是你忘了加 box-sizing: border-box; 或者内容撑爆了 fr 单元。