先记住这个答案
grid-auto-flow 控制自动放置算法:row 按行依次放置,列方向按需扩展;column 按列依次放置;加 dense 后算法允许回填先前因大项留下的空洞,但会打乱文档顺序。默认是 row(稀疏),不设 dense 时只前进不回退,通常保留更多间隙。
- row 按行填满再换行,column 按列填满再换列。
- dense 允许回填空洞,但会改变视觉顺序。
- 不写 dense 是稀疏算法,只前进不回退。
自动放置的扫描顺序与回填机制
当网格项没有显式指定 grid-row-start 或 grid-column-start 时,自动放置算法按 grid-auto-flow 决定的主轴顺序分配位置。row 表示先填满第一行的每一列,再进入下一行;column 则反过来,先填满第一列的每一行再进入下一列。新增隐式轨道时,row 模式添加新行,column 模式添加新列。
dense 控制的是放置时能否“回头”。稀疏模式下,算法记录一个游标,只会向后移动,绝不回头;如果当前项放不进剩余空间,就跳过并继续,最终留下空洞。密集模式则允许已放置项之前的区域被后来的小项填充,代价是文档顺序被打乱。两种模式都只在 auto 放置项之间起作用,显式放置不受影响。
三列网格中大小项混合的放置对比
grid-auto-flow 决定自动放置的扫描顺序。row 表示逐行填充,先从第一行开始,依次放置直到该行放不下,再开始新行;column 则逐列填充,先纵向填满一列再换下一列。新增隐式轨道时,row 会在底部添加新行,column 会在右侧添加新列。
dense 控制是否允许回填。稀疏算法(默认)只在网格上向前移动,不会回到前面寻找空隙;若当前行剩余空间不足,就换到下一行,因此可能留下空洞。密集算法允许后来的小项填入之前留下的空位,但会改变项目的视觉顺序,使其与 DOM 顺序不一致。
举例:在一个 2×3 的显式网格(grid-template-rows: 100px 100px; grid-template-columns: 100px 100px 100px)中,依次放置四个自动项:A 跨 2 列,B 跨 2 列,C 占 1 列,D 占 1 列,所有项高均为 1 行。使用 row(稀疏):A 放在第 1 行第 1-2 列,第 1 行第 3 列剩余 1 列不足以放下 B,所以 B 换到第 2 行第 1-2 列,C 接着放第 2 行第 3 列,D 只能新增第 3 行第 1 列,结果第 1 行第 3 列空着。改用 row dense:C 在放置时回溯,可放到第 1 行第 3 列,D 则放到第 2 行第 3 列,网格被填满,但视觉顺序从 A、B、C、D(DOM 顺序)变为 A、C、B、D,说明 dense 会打乱顺序。
何时不适合使用 dense
当文档顺序有语义意义(如制表符导航顺序、屏幕阅读顺序)时,dense 会让视觉顺序与 DOM 顺序有偏差,导致键盘用户或读屏器用户感知的流程与实际可见顺序不同。CSS Grid 的自动放置不会改变 DOM 顺序,但 visual order 通过 grid 排列改变,而可访问性树默认跟随 DOM 顺序,这种不一致会造成困惑。因此,如果网页依赖顺序或需要严格的 tab 顺序,应避免 dense。
另一个边界是显式放置的干扰:如果部分项目使用 line-based 显式放置,自动放置算法会绕过这些区域,但 dense 只在自动放置项之间起作用,显式区域仍保留。混合使用 dense 可能产生不可预测的缺口,因为算法只对 auto 项生效。若要精确控制布局且兼顾顺序,应优先使用 grid-template-areas 或 line-based 放置,而不是依赖 dense。
容易答错的地方
- 误认为 dense 会增加额外行
- dense 不会新增轨道,它只是重新安排自动项来填充已有空隙。空隙存在于显式或隐式轨道中,dense 不扩展网格,新增轨道仍由 grid-auto-rows/columns 控制。
- 以为 row dense 等价于 column
- row dense 仍然逐行填充,只是允许回填;column 则按列扫描。它们对相同内容可产生完全不同的放置结果,不能互换。例如在 2x3 网格中,column 优先填列,会改变隐式轨道的方向。
面试官还会怎么问?
grid-auto-flow: column dense 与 flexbox 的排序有何区别?
grid-auto-flow 基于二维轨道,项目可以跨行或跨列;flexbox 是一维流,没有显式的行列网格。column dense 会在列间回填,而 flex-order 改变视觉顺序但只沿主轴排列,不能填充垂直方向空洞。
如果 grid-auto-rows 未设置,dense 如何影响隐式行的高度?
隐式行由 grid-auto-rows 决定,默认 auto,因此行高适应内容。dense 可能把不同高度的项目放入同一隐式行,导致小项目被拉伸或行高由最大项决定,但 dense 本身不改尺寸,仅是位置重排。
在密集模式下,如何让某个项目始终保持原始顺序?
不可能完全阻止 dense 的回溯,但可以对该项目使用显式放置(如 grid-column 或 grid-row),因为显式定位的项目不参与自动放置,从而不被 dense 调整。代价是需要手动管理位置。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。