CSS Grid 布局实战笔记
从网格轨道到对齐方式,再到几个高频布局模式的拆解,记录我在真实项目中用 Grid 解决布局问题的思考与踩坑。
Flexbox 解决的是「一维」的排列问题——一行或一列。但页面里真正难搞的布局大多是二维的:九宫格卡片、侧边栏 + 主内容、表头对齐。过去要么嵌套多层 Flex,要么动用各种 hack,直到 CSS Grid 出现,二维布局才有了原生的答案。
这篇文章不从头讲属性,而是从「我要实现什么布局」出发,记录几个高频场景的 Grid 解法,以及那些文档里不会提醒你的坑。
轨道:fr、minmax 与 auto
Grid 最核心的概念是「轨道(Track)」,也就是行列的尺寸定义。最常见的三个单位:
1fr:剩余空间的均分份数,等价于「把剩下没分配的空间按比例分掉」。auto:按内容实际尺寸。minmax(min, max):在最小值和最大值之间弹性变化,是响应式布局的关键。
.grid {
display: grid;
grid-template-columns: minmax(0, 1fr) 320px; /* 左侧自适应,右侧固定 320px */
grid-template-rows: auto 1fr auto; /* 典型的头部-内容-底部结构 */
gap: 1rem;
}
一个容易忽略的点:1fr 的最小值是 auto 而不是 0。当格子里的内容(比如一段长英文单词或一张大图)超过可用空间时,1fr 会被内容撑开,导致溢出。想让它严格按比例收缩,用 minmax(0, 1fr)。
响应式:auto-fit 与 auto-fill
「卡片数量随容器宽度自动增减」是 Grid 最迷人的能力,一行就能写完:
.cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 1.25rem;
}
auto-fill 与 auto-fit 的差别很多人分不清:
| 关键字 | 行为 |
|---|---|
auto-fill |
尽量多放空轨道,轨道数量固定,不占位内容时轨道留白 |
auto-fit |
放不下时压缩轨道,空轨道折叠为 0,内容撑满整行 |
一个典型场景:底部一行只有 2 张卡片时,auto-fill 会在右侧留下空白,auto-fit 则会让卡片拉宽铺满。看产品想要哪种效果再选,我因为这个差别在评审时来回改过两轮。
区域命名:让布局自己会说话
grid-template-areas 把布局写成「ASCII 画」,阅读成本极低,也方便整体调整:
.layout {
display: grid;
grid-template-columns: 220px 1fr;
grid-template-areas:
'sidebar main'
'sidebar footer';
height: 100vh;
}
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }
@media (max-width: 768px) {
.layout {
grid-template-columns: 1fr;
grid-template-areas:
'main'
'sidebar'
'footer';
}
}
改动布局时只需要改 grid-template-areas 里的字符排布,不需要去动各个子元素的定位属性。唯一要注意的是区域必须是矩形——不能写成 L 形或凹形,那是语法错误。
对齐:对齐属性到底作用在谁身上
Grid 的对齐属性一共 6 个,最容易记混的是它们分别作用于「网格容器」还是「网格项」:
| 属性 | 作用对象 | 轴 |
|---|---|---|
justify-content |
容器 | 主轴(行内轴) |
align-content |
容器 | 交叉轴(块轴) |
justify-items |
容器内所有项 | 行内轴 |
align-items |
容器内所有项 | 块轴 |
justify-self |
单个项 | 行内轴 |
align-self |
单个项 | 块轴 |
记忆口诀:带 -content 的是管整个网格的整体位置(轨道分布),带 -items/-self 的是管单个格子内部内容的位置。 当网格总尺寸小于容器尺寸(比如 grid-template-columns: repeat(3, 200px) 而容器很宽)时,用 justify-content 才能让整组轨道居中或分散,只设 justify-items 是没用的——这是新手最常见的困惑。
实战拆解:两栏后台布局
一个典型的后台管理页,往往由「固定侧边栏 + 弹性主区」组成,配合滚动区域:
.app {
display: grid;
grid-template-columns: 240px 1fr;
height: 100vh;
}
.sidebar {
overflow-y: auto;
background: #f6f6f4;
}
.main {
display: grid;
grid-template-rows: auto 1fr; /* 顶栏 + 可滚动内容 */
min-width: 0; /* 关键:允许内容区收缩 */
}
两个值得注意的坑:
1. 内容撑破网格。 主区里的表格或长文本默认不会主动收缩,需要给网格项设 min-width: 0(块轴上对应 min-height: 0),否则 1fr 轨道会被内容撑破,出现横向滚动条。
2. height: 100vh 与移动端的地址栏。 手机上 100vh 会比可视区域高,底部被遮住。后台页面若主要在桌面使用还好,如果要做移动适配,考虑 100dvh。
与 Flexbox 的分工
我现在的选择标准很简单:一维排列用 Flex,二维布局用 Grid,两者嵌套使用。比如卡片本身是 Grid 布局的,但卡片内部一排按钮的间距用 Flex 控制。不要神化 Grid、也不要什么都用 Grid——它是布局工具,不是信仰。
小结
Grid 把过去靠嵌套和 hack 才能实现的布局,变成了声明式的轨道定义。真正值得花时间掌握的是三个点:minmax(0, 1fr) 防溢出、auto-fill 与 auto-fit 的取舍、以及 -content 与 -items 的对齐分工。把这三点记住,绝大多数二维布局都能一次写对。
浏览器对 Grid 的支持已经超过十年了(IE10 时代的旧语法除外),放心用。