在现代 CSS 布局体系中,CSS Grid 可以说是网页布局的一场革命。而其中最为迷人(也最容易误解)的,就是 fr 单位了。很多开发者初学时以为 1fr + 2fr 就是按比例瓜分空间,看似简单,实则暗藏玄机。
今天我们就来剖析这个“玄学单位”——fr,以及它背后的剩余空间分配算法。
什么是 fr?
fr 是 fraction(分数) 的缩写,用于表示 可用剩余空间的比例单位。比如:
grid-template-columns: 1fr 2fr;
意味着:第一个列占用 1 份,第二列占用 2 份 —— 但这只是前提,不是全局的真理。
剩余空间?什么叫“剩余”?
这是最容易忽略的一个点:fr 只分配“剩余空间”,那么到底什么是剩余空间呢?
先减去以下这些:
- 所有固定宽度(如
100px) - 所有内容撑开的最小宽度(如
min-content、auto) - 所有
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
auto 和 fr 的冲突逻辑
现在看看下面这个例子:
grid-template-columns: auto 1fr;
这个布局中,如果第一列内容很多,第一列会先“吃掉”所需空间。剩下的才分配给 1fr。
换句话说:
auto会测量内容最小宽度- 剩余空间才给
fr
这也是很多人疑惑“为什么 1fr 看起来比 auto 还小”的原因。
min-content 和 fr 的故事
min-content 是个内容撑开的最小宽度值,在 grid 中它会优先保留。
grid-template-columns: min-content 1fr;
假设第 1 列内容为 “Supercalifragilisticexpialidocious”,它会尽可能展示不换行的长度。
你可以理解为:min-content 是先吃肉的亲戚,fr 是最后打扫餐桌的晚辈。
minmax() 中的 fr
经典用法:
grid-template-columns: minmax(100px, 1fr) 2fr;
这是很多人误解的地方。
规则是:
minmax()的1fr只在100px之后才起作用;- 如果总空间不够,
minmax(100px, 1fr)至少会保留 100px; - 一旦空间足够大,
1fr就开始分配剩余部分。
这是一个“有最低生活保障”的 fr。
核心算法解析(W3C标准)
根据 W3C CSS Grid Layout Module 文档,fr 单位的算法大致如下:
- 收集所有非 fr 项的固定值,包括
px、%、auto、min-content、max-content; - 减去这些非 fr 项所占的空间,得到“剩余空间”;
- 遍历所有
fr单位,计算总 fr 权重; - 将剩余空间按比例分配到每个
fr单元中; - 如果某列设置了
minmax(min, 1fr),并且1fr得到的宽度小于min,则 强制分配最小值,其余 fr 重新计算; - 如果某列设置了最大值,比如
max(200px, 1fr),并且1fr太大了超过了上限,也会触发回退机制。
这是一个**“先定再分,再回滚修正”**的动态系统。
实际案例:3列响应式布局
.container {
display: grid;
grid-template-columns: 1fr 3fr 1fr;
gap: 20px;
}
你会看到中间一列始终比两侧大,而当屏幕缩小时:
- fr 会自动重新分配
- 不需要媒体查询
- 保证了比例关系
这是 fr 的魅力:比 flex 更适合面向网格的场景。
冷知识:为什么 1fr 和 100% 不一样?
这是一个常见误区。
width: 100%;
意味着这个元素要占满容器宽度,不考虑兄弟元素。
而:
grid-template-columns: 1fr 1fr;
意味着在兄弟元素参与下公平分配。即使你只有一个元素,它也不会自动理解为“满宽”,而是等比参与。
常见误解与反直觉行为
- 误以为 fr = flex-grow
实际上 fr 不是 flex-grow。它基于网格整列空间,而不是内容动态膨胀。 - 误以为 fr 是绝对宽度的等价表达
不是。它是“相对剩余空间”的比例表达。 - 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 单元。