Grid布局:学习网格系统,构建二维复杂布局
说实话,刚接触前端布局那会儿,我总觉得Flexbox已经够用了。直到有一次做后台管理系统,要排一个复杂的仪表盘——图表、数据卡片、列表混在一起,用Flexbox嵌套了四五层,改一个地方全崩了。嗯,那时候我才意识到,二维布局这事儿,得靠Grid。
Grid到底是什么?
Grid布局,说白了就是给页面画格子。你把容器分成行和列,然后把元素往格子里一放,完事。跟Flexbox最大的区别是:Flex是一维的,要么横着排要么竖着排;Grid是二维的,横竖一起管。
核心概念就三个:
- 网格容器——你设了
display: grid的那个元素 - 网格项——容器里的直接子元素
- 网格线——划分行和列的线,从1开始编号
我在项目中遇到过一位同事,用Grid做表格布局,结果发现元素对不齐。后来一看,他把网格线编号搞混了。记住:网格线是从左上角开始数的,不是从0。
基础用法:三步上手
先看个最简单的例子。假设你要做一个三列等宽的布局:
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 20px;
}
这里 1fr 是Grid特有的单位,代表「剩余空间的一份」。三个1fr就是三等分。你想想看,如果用百分比,还得算边框和内边距,多麻烦。
行的话用 grid-template-rows,用法一样。不过有个小技巧:如果你不确定有多少行,可以用 auto,让内容自己撑开。
我的习惯:列宽用fr,行高用auto。除非你明确知道每行的高度,否则别写死。
二维布局的威力
Grid真正厉害的地方,是能同时控制行和列。来看一个典型的页面骨架:
.layout {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-rows: 60px 1fr 40px;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
height: 100vh;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }
看到 grid-template-areas 了吗?这是我最喜欢的功能。直接用字符串画布局,哪里是头部、哪里是侧边栏,一目了然。我曾经用这个给一个电商后台重构布局,原来500行的CSS缩到了80行。
网格项的定位
有时候你需要某个元素跨多行或多列。比如一个图片要占两列:
.featured-image {
grid-column: span 2;
/* 或者指定起止线:grid-column: 1 / 3; */
}
这里要注意:span 2 表示跨越2列,而 1 / 3 表示从第1条线到第3条线。效果一样,但写法不同。我个人习惯用 span,因为更直观——「我要占两格」。
我曾经踩过的坑:用 grid-column: 1 / 3 时,如果网格只有2列,那没问题。但如果后来改成3列,这个元素就只占前两列了。用 span 的话,它会自动适应。所以,除非你有明确的线编号需求,否则优先用 span。
响应式设计:自动填充与自适应
Grid做响应式特别顺手。比如你要做一个卡片列表,桌面端4列,平板2列,手机1列。传统做法得写三个媒体查询。用Grid的话:
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 16px;
}
这一行代码就搞定了。auto-fill 会自动计算能放几列,minmax(250px, 1fr) 保证每列至少250px,多了就均分。你想想看,这比写媒体查询省了多少事?
不过有个细节:auto-fill 和 auto-fit 的区别。前者会保留空列,后者会折叠空列。如果你希望卡片始终靠左对齐,用 auto-fit。
对齐与间距
Grid的对齐分两个维度:
- 容器内对齐:
justify-items(水平)、align-items(垂直) - 网格项自身对齐:
justify-self、align-self
举个例子,你想让所有网格项居中:
.container {
justify-items: center;
align-items: center;
}
如果只想让某个按钮靠右:
.button {
justify-self: end;
}
间距用 gap 属性,它是 row-gap 和 column-gap 的简写。我建议统一用 gap,除非你行间距和列间距不一样。
性能调优:别让Grid拖慢页面
Grid本身性能很好,但用不对也会出问题。我总结了几条经验:
| 场景 | 建议 | 原因 |
|---|---|---|
| 大量网格项(100+) | 用 grid-auto-flow: row dense 配合虚拟滚动 |
避免一次性渲染所有元素 |
| 嵌套Grid | 最多嵌套3层 | 超过3层,浏览器重排成本剧增 |
| 动态改变网格结构 | 用CSS类切换,别直接操作style | 类切换触发重排,但比style操作高效 |
| 图片在Grid中 | 给图片设 width: 100%; height: auto |
防止图片撑破网格,导致布局抖动 |
避坑指南:我曾经在一个数据大屏项目里,用Grid排了200多个图表卡片。结果页面加载要3秒。后来发现是每个卡片都用了 grid-template-rows: 1fr,导致浏览器要反复计算行高。改成 auto 后,加载时间降到了0.8秒。
实战:一个完整的仪表盘布局
来,咱们把前面学的串起来。做一个四区域的仪表盘:
.dashboard {
display: grid;
grid-template-columns: 280px 1fr 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"sidebar header header"
"sidebar main aside"
"sidebar footer footer";
gap: 12px;
min-height: 100vh;
padding: 16px;
}
/* 响应式:小于768px时变单列 */
@media (max-width: 768px) {
.dashboard {
grid-template-columns: 1fr;
grid-template-areas:
"header"
"main"
"aside"
"sidebar"
"footer";
}
}
你看,一个媒体查询就搞定了从三列到单列的切换。如果用Flexbox,你得改HTML结构或者写一堆覆盖样式。
知识体系总览
下面这张图,是我自己总结的Grid知识框架。每次做新项目前,我都会扫一眼:
这张图把Grid分成了四个模块:容器属性、项目属性、单位与函数、性能调优。每次做布局前,先想清楚你要用哪个模块的能力。比如要做响应式,重点看「单位与函数」;要做复杂区域划分,重点看「容器属性」。
写在最后
Grid布局学起来不难,但用好需要练习。我的建议是:从今天开始,所有新项目的布局都用Grid写。哪怕只是一个简单的两栏,也试试Grid。用上一个月,你就回不去Flexbox了。
记住一句话:Flexbox管一维,Grid管二维。选对工具,事半功倍。