CSS Subgrid 解决了什么问题?它和普通 Grid 嵌套有什么区别?
简化版
普通嵌套 Grid 会创建自己的独立轨道,兄弟卡片内部的标题、正文和按钮难以跨卡片对齐;Subgrid 让子网格沿某个轴复用父网格的轨道尺寸与线。它适合卡片、表单和复杂二维对齐,但只能继承父网格已跨越的轨道范围,间距和隐式轨道也要单独理解。
详细版
子元素先是父 Grid 的一个网格项,再对自身设置 display: grid 与 grid-template-rows: subgrid 或列方向 subgrid。该轴不再创建独立显式轨道,而是采用父网格对应轨道的尺寸与网格线,内部后代可据此对齐。
Subgrid 可以只用于行或列,另一轴仍定义自己的轨道。它会继承父级 gap,但子网格可覆盖 gap;命名网格线也能传递和补充。若父级没有提供足够轨道,Subgrid 不会凭空突破自己跨越的范围。
它解决的是共享轨道对齐,不是组件所有样式继承,也不是瀑布流或容器查询的替代品。
完整版教学
一、普通嵌套 Grid 为什么对不齐
假设三张商品卡都用 grid-template-rows: auto 1fr auto,每张卡会根据自己的标题和正文独立计算三行。第一张标题一行、第二张标题两行时,各卡正文起点不同,按钮也可能无法处于同一水平线。
卡片 A:标题 24px | 正文 96px | 按钮 40px
卡片 B:标题 48px | 正文 72px | 按钮 40px
↑ 每张卡内部独立算轨道
把卡片设成等高只能对齐外边界,不能让内部每一行共享基准。过去常用固定标题高度或 JavaScript 测量,都会对字体缩放和内容变化产生脆弱依赖。
二、Subgrid 让后代参与祖先的轨道体系
父网格先定义列和行,卡片作为网格项跨越若干父行;卡片自身启用 subgrid 后,其后代可以落在父级对应行上。这样所有卡片标题共同影响标题行高度,所有按钮共享按钮行。
.cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto 1fr auto;
}
.card {
display: grid;
grid-row: span 3;
grid-template-rows: subgrid;
}
若最长标题需要 48px,共享标题轨道统一为 48px;正文行吸收剩余空间,按钮自然对齐。这里的关键不是子网格“复制”父尺寸,而是它直接参与同一次轨道计算。
三、Subgrid 可以只启用一个轴
很多布局只需要列对齐,而行高仍应由组件独立决定。可以在列轴使用 subgrid,行轴继续声明 auto 1fr auto,反过来也可以。
.form-group {
display: grid;
grid-template-columns: subgrid;
grid-column: 1 / -1;
grid-template-rows: auto;
}
一个两列表单中,所有字段标签共享第一列最大宽度,输入区共享第二列;每个字段的错误信息高度却可自行增长。按轴选择能避免为了一个对齐需求把整个组件都绑死在父网格上。
四、可用轨道由子项跨越范围决定
Subgrid 只能使用父网格项所跨越的那几条轨道。若父网格有 12 列,而组件只占第 4 到第 8 列,它的 subgrid 只获得这段范围,内部网格线编号从自己的局部视角重新计算。
父列线:1 2 3 [4 5 6 7 8] 9 10 11 12 13
子网格: [1 2 3 4 5]
子网格不能用声明额外显式轨道的方式超出范围;内容若需要更多空间,会按网格放置规则处理或溢出。设计 API 时应让组件明确知道自己预期跨几列或几行。
五、gap 和命名网格线会沿体系传递
Subgrid 默认采用父网格的间距,这能保持整体节奏;也可以在子网格上声明新的 gap,此时轨道线仍共享,但子内容在边缘附近的可用空间会相应调整。命名线可以从父级获得,子网格也能为局部使用添加名称。
| 能力 | 普通嵌套 Grid | Subgrid |
|---|---|---|
| 轨道尺寸 | 子级独立计算 | 与父级共享 |
| 兄弟内部对齐 | 难 | 天然支持 |
| gap | 自己定义 | 默认继承,可覆盖 |
| 网格线 | 独立编号和名称 | 映射父线并可补充名称 |
| 超出父级跨度 | 可自建轨道 | 受跨度范围限制 |
团队若用命名线如 [label-start]、[control-start],比记数字列线更容易维护。布局重排时语义名称也比硬编码 grid-column: 2 / 4 稳定。
六、适用场景和渐进增强
卡片内部对齐、定义列表、复杂表单和杂志排版是典型场景。若只需要一维排列或各组件内容完全独立,Flexbox 或普通 Grid 更简单,不必为了使用新能力增加父子耦合。
@supports (grid-template-rows: subgrid) {
.card {
grid-template-rows: subgrid;
}
}
例如 4 张卡片、每张 3 个内部区块,传统测量方案要观察 12 个区域并在字体或容器变化时重算;Subgrid 交给布局引擎一次求解。仍需依据项目支持矩阵验证目标浏览器,并准备可接受但不完全对齐的降级布局。
记忆钩子:普通嵌套 Grid 是“每户自己画坐标纸”,Subgrid 是“子组件借用整栋楼的轴线”。
七、常见误区与追问
- 误区:Subgrid 会继承父网格的所有 CSS。 它共享指定轴的轨道,不等于继承颜色、边框或全部布局声明。
- 误区:使用 Subgrid 后不必声明子项跨度。 子网格能获得多少轨道,正由它作为父网格项的跨度决定。
- 误区:Subgrid 是 Flexbox 的升级版。 它解决二维共享轨道,Flexbox 仍更适合单轴内容分配。
- 追问:可以只对行使用 Subgrid 吗? 可以,列轴和行轴能独立选择
subgrid或普通轨道定义。 - 追问:Subgrid 能实现 Masonry 吗? 不能直接等同;瀑布流关注不规则密集装箱,Subgrid 关注父子轨道对齐。
- 追问:父级 gap 会怎样处理? 默认传入子网格,子级可覆盖,但覆盖后边缘空间计算需要视觉验证。
八、加强记忆
先识别问题是否真的是“不同组件内部需要共享同一组线”。如果只是卡片等高,普通 Grid 已足够;如果标题、正文、按钮都要跨卡片逐行对齐,Subgrid 才体现价值。实现时让父级定义轨道,让卡片跨越所需范围,再按行或列启用 subgrid;记住共享的是轨道体系,范围受父级跨度限制,gap 与命名线可以沿体系传递。这样能用 CSS 原生布局消除固定高度和脚本测量。