最简可靠的正方形网格方案是用 aspect-ratio: 1 / 1 配合 vh 单位(如 grid-template-columns: repeat(3, 33.33vh) 和 height: 100vh),避免百分比行高失效问题;内容动态时需 JS 测量真实宽度并重设列宽;旧浏览器可用 padding-bottom: 100% + 绝对定位兜底。

直接用 aspect-ratio: 1 / 1 是最简、最可靠的起点,但仅靠它还不够——Grid 轨道分配和内容渲染顺序会破坏正方形效果,尤其在视口变化或内容动态加载时。
为什么 grid-template-rows: 33% 完全失效
百分比高度在 Grid 中不按你想象的方式工作。当父容器(比如 body)没有显式 height,grid-template-rows: 33% 33% 33% 实际计算为 0px,所有格子塌缩成内容高度。这不是 bug,是 CSS 规范定义的行为:百分比行高必须有可计算的块级祖先高度作为基准,而默认文档流里这个链路是断裂的。
常见错误现象包括:
- 格子高度随文字行数膨胀,宽高比完全失控
- 加了
height: 100%但毫无反应 - 调试时发现
getBoundingClientRect().height远大于预期
用 vh + aspect-ratio 构建视口锚定的正方形网格
这是目前兼容性好(Chrome 88+、Firefox 89+、Safari 15.4+)、无需 JS、且真正响应式的解法。核心是放弃依赖父高,改用视口单位建立尺寸基准,再用 aspect-ratio 锁定比例。
关键参数说明:
-
grid-template-columns: repeat(3, 33.33vh)—— 每列宽度 = 视口高度的 1/3;配合height: 100vh,容器高度即为视口高度,自然推导出每格理论宽度 ≈ 高度 -
aspect-ratio: 1 / 1—— 强制每个.square无论内容多少,都维持 1:1 宽高比 - 避免用
gap过大:若设gap: 20px,三列总宽会超过100vh,导致横向滚动或换行;建议gap: 0或小值如2px
示例最小可行代码:
* { margin: 0; padding: 0; box-sizing: border-box; }
.cont {
display: grid;
grid-template-columns: repeat(3, 33.33vh);
height: 100vh;
}
.square {
aspect-ratio: 1 / 1;
outline: 1px solid #333;
}
当内容不可控或需严格约束容器不溢出时,必须加 JS 测量
如果格子里可能塞入多行文本、图片或第三方组件,aspect-ratio 仍会拉高单元格,而 Grid 的 1fr 轨道不会自动收缩来匹配——结果就是整行被撑高,容器溢出视口底部。
此时不能只信 CSS,得让 JS 把真实渲染后的宽度“反哺”给 Grid 轨道:
- 在
DOMContentLoaded或requestAnimationFrame中调用测量,确保aspect-ratio已生效 - 用
getBoundingClientRect().width获取首个格子实际宽度(四舍五入到整数,避免 sub-pixel 渲染抖动) - 写回
style.gridTemplateColumns = `repeat(3, ${width}px)`,彻底绕过 fr 计算逻辑 - 窗口 resize 或内容更新后,必须重新触发该函数——否则新尺寸不会同步
这步容易被忽略,但恰恰是生产环境稳定性的分水岭。
旧浏览器兜底:padding-bottom 百分比方案
如果你仍需支持 Safari 14.1 以下、Firefox 88 以下或 IE11,aspect-ratio 不可用,就得回归盒模型老办法:
- 每个格子设
position: relative; padding-bottom: 100%(基于父宽计算,永远可靠) - 内部内容用
position: absolute; top: 0; left: 0; width: 100%; height: 100%填满 - Grid 父容器照常设置
grid-template-columns: repeat(3, 1fr),子项的 padding 会自动驱动高度
缺点是结构略重、绝对定位内容需额外处理 z-index 和交互,但它是目前唯一能在所有浏览器中 100% 稳定产出正方形的纯 CSS 方案。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











