在网页开发里,九宫格是常见的图片墙、功能入口或仪表盘布局。过去要实现横竖各三格且自适应,往往要写一堆百分比宽度、margin负值或者多层flex容器,稍不注意就会在最后一行出现对齐偏差。CSS Grid布局引入之后,这类问题可以从轨道定义层面一次性解决,不需要再纠结每个子项的单独尺寸。
理解fr单位是使用网格的前提。fr全称fraction,意为“剩余空间的一份”。当容器宽度确定后,浏览器先减去固定尺寸和间隙,再把剩下的空间按fr比值分配。比如grid-template-columns: 1fr 1fr 1fr表示三列均分剩余空间。它和百分比不同,百分比不考虑gap,而fr会自动在分配时避开gap占用的尺寸,因此用fr搭九宫格不会出现总和溢出。
如果手动写三个1fr显得重复,repeat函数就派上用场。repeat的语法是repeat(次数, 轨道尺寸),因此repeat(3, 1fr)等价于写三遍1fr。这种写法在列数变化时只需改一个数字,比复制粘贴更不容易出错。配合grid-template-rows: repeat(3, 1fr)就能在垂直方向也建立三等分,从而让容器变成严格的九宫格。
下面是一段最小可用的九宫格代码,容器设了固定宽高仅为演示,实际项目中可改为自适应尺寸:
.container {
display: grid;
width: 300px;
height: 300px;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
gap: 10px;
}
.item {
background: #e0e0e0;
}
这段样式中,gap统一控制格子间距,不需要给每个子项写margin,也不会出现外边距合并导致的错位。子项只要放进容器,就会按顺序自动填入第一行的三列、第二行的三列,直到第九个填满。如果子项不足九个,空白轨道依然保留,不会让后面的格子往前挤。
传统方案与Grid方案的对比
在Grid普及前,开发者常用float加width百分比做九宫格。比如每个子项设width: 33.33%再左浮动,然后用nth-child(3n+1)去掉左边距。这种方式要计算百分比减去间隙后的精确值,且父容器清除浮动必不可少。一旦用户调整视口,间隙若用固定像素,百分比宽度就和实际可用宽度不匹配,最后一行容易向右偏移。
另一种做法是flex嵌套:外层flex横向排三个子容器,每个子容器内部再flex纵向排三个。这带来三层DOM结构,样式分散在父与子,后期改列数要同时动两处。而Grid只用一层容器加两个repeat声明,DOM保持干净,布局逻辑集中。从可维护性看,Grid明显胜出。
用table实现虽然能对齐,但语义错误,且单元格间隙用border-spacing控制,难以做响应式重排。下面用表格列出三种方式在九宫格场景的核心差异:
| 方案 | DOM层数 | 间隙处理 | 改列数成本 |
|---|---|---|---|
| float百分比 | 1层 | 手动减宽或负边距 | 高,需改多处宽度 |
| flex嵌套 | 3层 | 子容器padding | 中,改两处flex |
| Grid repeat | 1层 | gap统一 | 低,改一个数字 |
从表中能看到,Grid在每一项上都更省力。尤其是gap属性让水平和垂直间隙使用同一逻辑,不再需要针对行列写不同修正。这对快速搭建原型尤其有用。
用repeat和fr实现自适应与变体
九宫格不总是固定大小,很多场景要求宽度随屏幕变化。此时把容器宽度设为100%,fr依然能均分。若希望格子有最小尺寸,可混用minmax与repeat:repeat(3, minmax(80px, 1fr))表示每列至少80像素,空间富余时再伸展。这样在手机上三列不会挤成无法点击的小块。
repeat还支持auto-fill或auto-fit实现不确定数量的格子。虽然九宫格是固定九个,但用repeat(auto-fit, minmax(100px, 1fr))可以让容器自动决定一行放几个,内容少时单行排列,多时自动换行,这种思路能复用同一套样式做不同数量网格。下面展示一个带minmax的样式例子:
.wrap {
display: grid;
width: 100%;
grid-template-columns: repeat(3, minmax(80px, 1fr));
grid-auto-rows: 1fr;
gap: 12px;
}
这里grid-auto-rows保证隐式行也按1fr走,避免内容不等高时行高塌陷。若要在某格跨列或跨行,比如中间放一个大图占四格,只需给该子项加grid-column: span 2; grid-row: span 2;,其余格子自动重排,这是float方案很难低成本实现的。
实际写的时候建议把列定义和间隙抽成CSS变量,例如--cols: 3然后写repeat(var(--cols), 1fr),配合媒体查询改--cols就能在平板变两列、手机变一列。这种写法把布局参数化和Grid声明结合,比反复重写样式表更清晰,也符合现代CSS工程化习惯。
常见误区与调试建议
刚接触fr的人会把它当成百分比,于是给九宫格写repeat(3, 33.33%)再加gap,结果容器被撑破。要记住fr分配的是“剩余空间”,而百分比是“总空间的占比”,两者在存在gap时数学含义不同。如果非要用百分比,必须手动减去gap总宽,而fr帮我们免掉这步。
另一个误区是认为repeat只能写固定次数。其实repeat第二个参数可以是多值模式,如repeat(2, 1fr 2fr)会生成1fr 2fr 1fr 2fr共四列,适合做宽窄相间的画廊。九宫格虽不需要,但了解它能避免把repeat想得太局限。调试时打开浏览器网格检视工具,能直接看到轨道线和fr分配比例,比猜样式直观得多。
若发现格子没有排成九宫而是竖着排成一列,通常是因为忘了设grid-template-columns,此时所有子项默认挤在单列里。补上repeat(3, 1fr)即可。还有人把height写死而内容溢出,这时用min-height搭配grid-auto-rows更稳妥。掌握这些细节后,九宫格布局就不再是需要复制旧项目的麻烦事,而是几行声明就能交付的结构。