Flexbox 解决了“一行或一列里怎么摆”的问题,但当你需要同时控制行与列时,它会立刻露出疲态。CSS Grid 是第一个真正意义上的二维布局系统:它把页面切成一张可以被精确寻址的网,让“跨行跨列”“区域命名”“自动填充”“密度打包”这些过去要靠浮层与 JS 计算才能做到的事情,变成几行声明式代码。但 Grid 也是所有 CSS 模块中概念密度最高的一个——fr 到底怎么分配?auto-fill 和 auto-fit 差在哪?grid-auto-flow: dense 会带来什么副作用?subgrid 解决了什么历史遗留问题?本篇 25 分钟深度长文,从网格线的编号规则讲到子网格与容器查询,配合四个可交互的实时实验室,帮你把 Grid 从“会用”推进到“用对、用巧、用稳”。
为什么我们需要二维布局系统
要理解 Grid 的价值,先回顾一下在它之前我们是怎么做页面框架的。2000 年代流行 float 布局,2010 年前后流行 inline-block,再后来是 table-cell。这些方案的共同点是:它们都不是为二维布局设计的,我们只是把一维工具硬拼成了二维效果。
.col {
float: left;
width: 33.333%;
}
.clearfix::after {
content: "";
display: block;
clear: both;
}
/* 时代二:inline-block + 恼人的空白间隙 */
.col-ib {
display: inline-block;
width: 32%; /* 不敢写 33.33% */
vertical-align: top;
}
/* 时代三:Flexbox,一维很强,二维很累 */
.flex-wrap-layout {
display: flex;
flex-wrap: wrap;
}
/* 想让第二行第一项跨到上一行?做不到。 */
/* 时代四:Grid,二维一等公民 */
.grid-layout {
display: grid;
grid-template-columns: 200px 1fr 1fr;
grid-template-rows: 80px auto 60px;
gap: 16px;
}
Grid 的关键突破在于:它把容器变成了一个坐标系,而子元素可以通过坐标来定位。这从根本上改变了布局的心智模型——从“把元素挤到某个位置”变成“把元素放到某条线上”。
心智模型:线、轨道、单元格、区域
Grid 的全部概念都可以用四个词概括。理解它们的层级关系,是后续所有高级用法的地基。
1 开始编号,负值从右/下反向计数
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
}
/* 用正数线号定位:从第 2 条列线到第 4 条列线 */
.item-a {
grid-column: 2 / 4; /* 跨越 2 列 */
grid-row: 1 / 2;
}
/* 用负数线号定位:倒数第 1 条线 */
.item-b {
grid-column: 1 / -1; /* 横跨整行 */
}
/* 用 span 关键字:从当前位置跨 2 列 */
.item-c {
grid-column: span 2;
}
/* 简写:grid-area: row-start / col-start / row-end / col-end */
.item-d {
grid-area: 1 / 1 / 3 / 4;
}
每个单元格的 行 / 列 起始线号
最容易搞混的一点:线号 vs 序号
grid-column: 2 / 4 里的 2 和 4 是网格线的编号,不是“第 2 列到第 4 列”。因为 3 列网格只有 4 条列线,所以 2 / 4 实际占据的是第 2、3 两列。记住一句话:起始线是含的,结束线是不含的。
显式网格与隐式网格:溢出时发生了什么
当你用 grid-template-columns 和 grid-template-rows 定义网格时,你创建的是显式网格。但只要有子元素超出这个范围,浏览器就会自动创建隐式网格轨道来容纳它们——而这正是很多“布局莫名奇妙被撑开”问题的根源。
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
/* 10 个子元素 → 自动生成 4 行隐式轨道 */
/* 这些隐式行的高度由内容决定(auto) */
/* 修复:显式控制隐式轨道尺寸 */
.grid-fixed {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 120px; /* 所有隐式行统一高度 */
}
/* 更常用:隐式行高度随内容,但有下限 */
.grid-auto {
grid-auto-rows: minmax(120px, auto);
}
/* 方向也可以反转:按列自动排列 */
.grid-column-flow {
display: grid;
grid-template-rows: repeat(3, 100px);
grid-auto-flow: column;
grid-auto-columns: 150px;
}
grid-template-* 定义,尺寸完全可控
auto
一条实用经验:只要你的网格子元素数量不固定(比如从接口拉数据渲染),就应该显式声明 grid-auto-rows。否则当内容长度不一致时,每一行的高度都会各不相同,视觉上非常凌乱。
fr 单位的真相:它分配的是剩余空间
fr 是 Grid 引入的新单位,代表“自由空间的一份”。但很多人误以为它会平均分配容器总宽度,于是写出了看似合理、实则溢出容器的代码。
.wrong-idea {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 20px;
}
/* 实际:先扣掉 2 个 gap = 40px,剩下的才三等分 */
/* fr 与固定尺寸混用:固定值优先扣除 */
.mixed {
display: grid;
grid-template-columns: 240px 1fr 1fr;
/* 容器 1000px:先扣 240,剩下的 760 两等分 → 380px */
}
/* 不同比例:1fr 2fr 1fr = 1:2:1 */
.ratio {
grid-template-columns: 1fr 2fr 1fr;
}
/* ⚠️ 经典溢出问题:长内容撑破 fr 轨道 */
.overflow-bug {
display: grid;
grid-template-columns: 1fr 1fr;
}
/* 如果子元素里有超长英文单词或 nowrap 文本 */
/* 1fr 的最小尺寸是 min-content,会被撑大 */
/* ✅ 修复:显式把最小值设为 0 */
.fixed-overflow {
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}
/* 其他尺寸关键字 */
.keywords {
grid-template-columns:
min-content /* 最窄,按最长单词 */
max-content /* 最宽,按内容不换行 */
fit-content(200px); /* 上限 200px */
}
minmax(auto, 1fr),最小值为 auto
结论:在所有可能出现长文本的网格里,把 1fr 换成 minmax(0, 1fr) 是一个几乎零成本的保险措施。它不会改变正常情况下的渲染结果,但会在极端内容下避免整个页面被撑出横向滚动条。
auto-fill 与 auto-fit:一字之差,天壤之别
这是 Grid 中最优雅、也最容易被误用的两个关键字。它们让网格可以根据容器宽度自动决定列数,从而实现真正的“无媒体查询响应式”。但二者在容器有富余空间时的表现完全不同。
建议按下面的顺序亲手试一遍:
- 把项目数量调到 2,最小宽度保持 120px,观察容器里剩下的空间去了哪里;
- 保持项目数量不变,点击切换到
auto-fit,看两个项目如何“膨胀”填满整行; - 把项目数量调到 10,此时两者表现完全一致——因为没有空轨道可折叠;
- 拖动最小宽度滑块,感受列数在某个临界点突然变化的过程。
.fill {
display: grid;
grid-template-columns:
repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
}
/* 容器 1000px → 4 条轨道,即使只有 2 个元素 */
/* 后两条轨道是空的,元素不会拉伸 */
/* auto-fit:空轨道会被折叠为 0 */
.fit {
display: grid;
grid-template-columns:
repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
}
/* 容器 1000px,只有 2 个元素 → 折叠后 */
/* 2 条轨道各占约 492px,元素被拉伸 */
记忆口诀:auto-fill 是“有多少位置就占多少位置”,auto-fit 是“把空位置收掉,让有内容的元素长大”。在绝大多数字内容网格场景中,auto-fit 是更符合直觉的选择。
命名网格线与 grid-template-areas
用数字坐标定位元素,代码可读性很差——三个月后你自己都看不懂 grid-area: 2 / 1 / 4 / 3 是什么意思。Grid 提供了两种提升可读性的武器:命名网格线和命名区域。
.named-lines {
display: grid;
grid-template-columns:
[start] 200px [content-start] 1fr [content-end] 200px [end];
}
.sidebar { grid-column: start / content-start; }
.main { grid-column: content-start / content-end; }
/* 同一条线可以有多个名字,用空格分隔 */
grid-template-columns:
[aside-end main-start] 1fr [main-end];
/* 方案 B:命名区域(推荐,可读性最高) */
.layout {
display: grid;
grid-template-columns: 200px 1fr 200px;
grid-template-rows: 64px 1fr 56px;
grid-template-areas:
"header header header"
"nav main aside"
"footer footer footer";
}
.layout > header { grid-area: header; }
.layout > nav { grid-area: nav; }
.layout > main { grid-area: main; }
/* 用点号表示空单元格 */
grid-template-areas:
"header header"
"main ."; /* 右下角留空 */
grid-template-areas 让布局在代码里“看得见”
下面这个实验室把三种常见的页面骨架做成了预设模板,点击按钮就能看到 grid-template-areas 如何让布局在几行代码之间完成整体重排。
注意观察:HTML 结构完全没变,只是 grid-template-areas 的值换了,页面骨架就从三栏变成了单列。这正是 Grid 相比传统方案最大的优势——布局逻辑与文档结构彻底解耦。
对齐系统全解析:items 与 content 的分工
Grid 的对齐属性一共有六个,它们两两成组,分工明确。搞不清它们的区别,是写出“看起来没生效”的 CSS 的主要原因。
观察要点:
justify-items与align-items只有在子项尺寸小于单元格时才能看出效果;设置stretch时子项会填满整个单元格;justify-content与align-content只有在轨道总尺寸小于容器时才能看出效果——这也是很多人在自适应网格里设置它们却“没反应”的原因(因为1fr已经吃掉了所有剩余空间);- 当轨道使用了
1fr时,容器里没有剩余空间,*-content系列属性自然无从发挥。
.center-grid {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(2, 100px);
justify-content: center; /* 整块网格水平居中 */
align-content: center; /* 整块网格垂直居中 */
gap: 12px;
}
/* 单个子项在单元格内居中 */
.item-center {
justify-self: center;
align-self: center;
}
/* 简写:place-* 系列 */
.short {
place-items: center; /* align-items + justify-items */
place-content: center; /* align-content + justify-content */
place-self: center; /* align-self + justify-self */
}
/* 使用 safe 关键字避免内容溢出被裁切 */
.safe-center {
place-items: safe center;
}
作用于每个子项在其单元格内的位置
作用于整组轨道在容器内的分布
一个管“格子里的东西”,一个管“格子本身”
自动放置算法与 dense 打包
当你没有给子元素指定明确位置时,Grid 的自动放置算法会按顺序把它们填进网格。默认行为是逐行填充,但你可以通过 grid-auto-flow 改变这个策略。
.flow-row {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-flow: row;
}
/* 逐列填充:先填满一列,再填下一列 */
.flow-column {
grid-auto-flow: column;
}
/* dense:让后面的小元素回填前面的空洞 */
.flow-dense {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-flow: dense;
}
.big { grid-column: span 2;
grid-row: span 2; }
/* 也可以组合使用 */
.flow-combo {
grid-auto-flow: column dense;
}
使用建议:如果内容顺序对用户有意义(比如文章列表、商品排序),不要使用 dense。如果内容本身就是无序的(比如相册、装饰性图片墙),dense 可以放心使用。另外,dense 只影响视觉呈现,不会改变 DOM 顺序,屏幕阅读器读到的仍然是源码顺序。
subgrid:让子孙元素对齐祖先的网格
这是 Grid 规范里最令人期待的特性之一,也是长期困扰 CSS 的“嵌套对齐”问题的终极答案。
想象一个经典场景:你有一个卡片网格,每张卡片包含标题、描述和底部按钮。由于描述长度不同,各张卡片的按钮位置参差不齐。在 subgrid 出现之前,唯一的解法是给卡片设固定高度,或者用 JS 测量对齐——两者都不优雅。
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
/* 子网格:让卡片内部的行对齐到父网格的行 */
.card {
display: grid;
grid-row: span 3;
grid-template-rows: subgrid; /* ✅ 继承父网格的行 */
grid-template-columns: subgrid; /* 列也继承 */
}
/* 父网格必须先定义好行轨道 */
.card-grid-with-rows {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto auto auto;
grid-auto-rows: auto;
}
/* 只继承一个轴也可以 */
.card-rows-only {
display: grid;
grid-template-rows: subgrid;
grid-template-columns: 1fr;
}
如果项目需要兼容更早的浏览器,可以用 @supports 做渐进增强:不支持 subgrid 时退化为普通网格,支持时再启用对齐。这样既保证了基础体验,又能在现代浏览器上获得更好的视觉效果。
.card {
display: grid;
grid-template-rows: auto 1fr auto; /* 降级方案 */
}
@supports (grid-template-rows: subgrid) {
.card {
grid-row: span 3;
grid-template-rows: subgrid;
}
}
响应式网格的四种武器
Grid 时代做响应式,比媒体查询时代从容得多。下面四种手段,从轻到重依次是:无媒体查询的自适应、媒体查询重排、容器查询、以及动态尺寸函数。
/* Repeat, Auto, Minmax */
.ram-grid {
display: grid;
grid-template-columns:
repeat(auto-fit, minmax(min(280px, 100%), 1fr));
gap: clamp(12px, 2vw, 24px);
}
/* min(280px, 100%) 保证极窄屏也不会溢出 */
/* 武器二:媒体查询重排 */
.page {
display: grid;
grid-template-columns: 1fr;
grid-template-areas:
"header" "main" "aside" "footer";
}
@media (min-width: 900px) {
.page {
grid-template-columns: 220px 1fr 260px;
grid-template-areas:
"header header header"
"aside main main"
"footer footer footer";
}
}
/* 武器三:容器查询 —— 组件级自适应 */
.card-wrap {
container-type: inline-size;
container-name: card;
}
.card-inner {
display: grid;
grid-template-columns: 1fr;
gap: 12px;
}
@container card (min-width: 420px) {
.card-inner {
grid-template-columns: 140px 1fr;
}
}
/* 武器四:动态尺寸函数 */
.dynamic {
grid-template-columns:
repeat(auto-fit, minmax(clamp(220px, 25vw, 320px), 1fr));
gap: clamp(8px, 1.5vw, 20px);
padding: clamp(16px, 4vw, 48px);
}
RAM 模式为什么这么重要
在 Grid 出现之前,一个卡片墙往往需要三到四个媒体查询断点,每个断点里写死列数。而 repeat(auto-fit, minmax(280px, 1fr)) 只用一行就表达了同样的意图,而且不需要维护断点——无论容器是 320px 还是 2560px,它都能给出合理的列数。这就是声明式布局的威力。
其中 minmax(min(280px, 100%), 1fr) 这个写法尤其值得一提:当容器宽度小于 280px 时,min(280px, 100%) 会取 100%,从而避免单列网格超出容器造成横向滚动。这一个小技巧,解决了很多移动端卡片墙的溢出顽疾。
Grid 还是 Flexbox:一份选型决策清单
这不是一个非此即彼的问题。现代项目中,两者几乎总是配合使用:Grid 负责页面骨架,Flexbox 负责组件内部。
.page {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-rows: 64px 1fr;
grid-template-areas:
"sidebar header"
"sidebar content";
}
/* 侧边栏内部:Flex 负责纵向排列菜单项 */
.sidebar-nav {
display: flex;
flex-direction: column;
gap: 4px;
}
/* 顶栏:Flex 负责两端对齐 */
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
}
/* 内容区:Grid 负责二维排布 */
.content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 20px;
}
place-items: center 更短,首选
flex: 1 更直观
三条判断规则
- 看维度:只需要在一行或一列里排列 → Flex;需要同时控制行和列 → Grid。
- 看内容与布局谁决定谁:布局由内容撑开 → Flex(content-out);布局先定好框架,内容再往里填 → Grid(layout-in)。
- 看可读性:如果一段布局的坐标关系需要靠注释才能理解,换用 Grid 的
grid-template-areas会更清晰。
实战一:圣杯布局与自适应卡片墙
这两个模式覆盖了日常开发中 70% 以上的 Grid 使用场景。把它们写熟练,Grid 的基本功就扎实了。
.holy-grail {
display: grid;
grid-template-columns: 200px minmax(0, 1fr) 200px;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header header"
"left main right"
"footer footer footer";
min-height: 100vh;
gap: 0;
}
/* 小屏降级为单列 */
@media (max-width: 768px) {
.holy-grail {
grid-template-columns: 1fr;
grid-template-areas:
"header"
"main"
"left"
"right"
"footer";
}
}
/* 自适应卡片墙:一行解决所有断点 */
.card-wall {
display: grid;
grid-template-columns:
repeat(auto-fit, minmax(min(300px, 100%), 1fr));
gap: 24px;
}
/* 卡片内部:用 Grid 让底部按钮对齐 */
.wall-card {
display: grid;
grid-template-rows: auto 1fr auto;
gap: 12px;
}
/* 1fr 让中间的描述区域吸收所有剩余高度 */
/* 底部的按钮自然就被推到了最下方 */
关键细节:为什么中间列要写 minmax(0, 1fr)
在圣杯布局中,中间列通常会放置长文本或代码块。1fr 的最小尺寸是 auto,一旦内容无法换行(比如一个超长的 URL 或一段 pre 代码),中间列就会强行撑宽,把两侧栏挤出屏幕。写成 minmax(0, 1fr) 后,最小值被强制设为 0,中间列会老老实实待在分配到的空间里,内容溢出交给 overflow 处理。
实战二:仪表盘与杂志式排版
当布局复杂度上升,Grid 的优势会指数级放大。下面这两个模式,用 Flexbox 实现会非常别扭,用 Grid 却几乎是一行一个。
.dashboard {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: 160px;
gap: 16px;
}
.dashboard .kpi-large {
grid-column: span 2;
grid-row: span 2;
}
.dashboard .kpi-wide {
grid-column: span 2;
}
.dashboard .kpi-tall {
grid-row: span 2;
}
/* 杂志式排版:不规则的分栏组合 */
.magazine {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 20px;
}
.magazine .headline {
grid-column: 1 / 5;
grid-row: span 2;
}
.magazine .feature {
grid-column: 5 / 7;
}
.magazine .full-width {
grid-column: 1 / -1; /* 横跨全部 6 列 */
}
.magazine .pull-quote {
grid-column: 2 / 5;
font-size: 1.4rem;
}
/* 用命名线简化杂志布局的定位 */
.magazine-named {
grid-template-columns:
[full-start] 1fr [main-start] repeat(4, 1fr) [main-end] 1fr [full-end];
}
.magazine-named .full-width {
grid-column: full;
}
一个实用技巧:在仪表盘里,给容器设置 grid-auto-rows: 160px,然后让不同的卡片 span 2 或 span 3,就能得到一个规整的“模块化网格”。所有卡片的高度都是同一基数的整数倍,视觉上非常整齐。这种“基数行”的思路,在杂志排版和图片墙中同样适用。
Grid 使用中的 8 个常见陷阱
以下这些坑,几乎每个用 Grid 的人都会踩一遍。提前知道,可以省下大量调试时间。
grid-column: 2 / 4 理解成“第 2 列到第 4 列”,实际是“第 2 条线到第 4 条线”
1fr 的最小值是 auto,长内容会撑破轨道,需要用 minmax(0, 1fr)
auto-fit 网格里用 justify-content: center,发现毫无效果
grid-area,导致自动放置算法完全失效
grid-auto-rows,隐式行高度参差不齐
grid-template-areas 里的同名区域不是矩形,整个声明被丢弃
dense 打包,破坏了键盘 Tab 顺序与阅读顺序
grid-template-columns,却忘了它自己也需要 display: grid
/* 此时 justify-content 无从发挥 */
.no-effect {
display: grid;
grid-template-columns:
repeat(auto-fit, minmax(200px, 1fr));
justify-content: center; /* ❌ 无效 */
}
/* 想在自适应网格中居中,改用固定宽度轨道 */
.works {
display: grid;
grid-template-columns: repeat(auto-fit, 200px);
justify-content: center; /* ✅ 有效 */
gap: 16px;
}
/* 陷阱 4:显式定位会让自动放置算法跳过该位置 */
/* 其余元素会自动填补剩余空位,但顺序可能出人意料 */
/* 排查方法:先注释掉 grid-area,看自然流下的顺序 */
!important,或者用 position: absolute 强行把元素拽到想要的位置。
调试技巧:让网格线看得见
Grid 的调试体验比 Flexbox 还要好,因为浏览器可以直接把网格线画在页面上。这是排查所有网格问题的第一手段。
.debug-grid > * {
outline: 1px dashed #ef4444;
outline-offset: -1px;
}
/* 技巧 2:给网格容器一个低透明度背景 */
.debug-grid {
background: rgba(37, 99, 235, 0.06);
background-image:
linear-gradient(to right, #cbd5e1 1px, transparent 1px);
background-size: 80px 100%;
}
/* 技巧 3:在页面上显示当前网格的关键参数 */
.debug-badge::after {
content: "grid: auto-fit / minmax(280px, 1fr)";
position: absolute;
bottom: 6px;
left: 6px;
font-size: 10px;
background: rgba(15, 23, 42, 0.8);
color: white;
padding: 2px 6px;
border-radius: 4px;
}
/* 技巧 4:用 @property 与自定义属性统一管理网格参数 */
:root {
--grid-min: 280px;
--grid-gap: 24px;
}
.managed {
display: grid;
grid-template-columns:
repeat(auto-fit, minmax(var(--grid-min), 1fr));
gap: var(--grid-gap);
}
grid 徽标可开启网格覆盖层
另外还有一个高频问题值得单独提:当网格看起来“没生效”时,先检查 display 是否被覆盖。在实际项目中,组件库的样式、工具类的 display: block、或者媒体查询里的重置,都有可能悄悄覆盖掉你的 display: grid。打开 Computed 面板确认最终生效值,往往比盯着源码看半天更有效。
性能与兼容性考量
Grid 的浏览器支持已经非常成熟。核心属性(grid-template-*、gap、place-items、auto-fit)在所有现代浏览器中都可放心使用。
/* display: grid —— 全平台支持 */
/* grid-template-areas —— 全平台支持 */
/* gap(grid 中) —— 全平台支持 */
/* minmax() / fit-content() —— 全平台支持 */
/* subgrid —— Chrome 117+ / FF 71+ */
/* container queries —— Chrome 105+ / Safari 16+ */
/* 避免深层嵌套:Grid 嵌套过深会拖慢布局计算 */
/* ❌ 三层嵌套只为了排三个卡片 */
.a { display: grid; }
.a > .b { display: grid; }
.a > .b > .c { display: grid; }
/* ✅ 一层 Grid 即可完成 */
.one-level {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 20px;
}
/* 用 will-change 谨慎优化动画中的网格元素 */
/* 滥用会导致图层爆炸,反而更慢 */
.animating-item {
will-change: transform;
transition: transform 0.3s ease;
}
/* 让网格子项具备良好的默认行为 */
.grid-item-safe {
min-width: 0;
min-height: 0;
overflow: hidden;
}
原生布局引擎处理,无 JS 参与
一次性计算轨道,不反复触发重排
核心属性全平台可用
布局意图直接写在代码里
一个容易被忽略的性能细节
当网格子项数量非常多(比如几百个)且使用了 auto-fit 时,浏览器需要为每一行重新计算轨道尺寸。虽然现代引擎对此做了大量优化,但在超长列表中,仍然建议配合虚拟滚动使用。Grid 解决的是布局问题,不是渲染数量问题——两件事不要混为一谈。
另外,给网格子项加上 min-width: 0 和 min-height: 0 是一个几乎零成本的保险:它可以让子项在必要时收缩,避免内容撑破轨道导致的连锁重排。
最佳实践清单与速查代码
把本文的核心结论浓缩成一份可以贴在工位上的清单。
- 自适应网格首选 RAM 模式:
repeat(auto-fit, minmax(min(280px, 100%), 1fr)) - 1fr 记得加 minmax(0, ...):防止长内容撑破轨道,尤其是在圣杯布局的中间列
- 显式声明 grid-auto-rows:子元素数量不固定时,避免行高参差不齐
- 优先用 grid-template-areas:可读性远超数字坐标,响应式重排也最方便
- 命名网格线做局部精细定位:方括号语法让
grid-column变得自解释 - 慎用 dense:会打乱视觉顺序,破坏键盘导航与阅读顺序
- *-items 管子项,*-content 管轨道:搞清区别再动手
- auto-fit 与 auto-fill 按需选择:要填满用 fit,要固定列宽观感用 fill
- 子项加 min-width: 0:避免内容撑破轨道引发连锁问题
- subgrid 用 @supports 渐进增强:老浏览器优雅降级
- Grid 搭骨架,Flex 排内容:两者配合,而不是互相替代
- 用 DevTools 网格覆盖层调试:先看清线,再改代码
/* 1. 自适应卡片墙 —— 最常用 */
.grid-1 {
display: grid;
grid-template-columns:
repeat(auto-fit, minmax(min(280px, 100%), 1fr));
gap: 20px;
}
/* 2. 页面骨架 —— 命名区域 */
.grid-2 {
display: grid;
grid-template-columns: 220px minmax(0, 1fr);
grid-template-rows: 64px 1fr auto;
grid-template-areas:
"sidebar header"
"sidebar main"
"sidebar footer";
min-height: 100vh;
}
/* 3. 仪表盘 —— 基数行 + 跨格 */
.grid-3 {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: 150px;
gap: 16px;
}
.grid-3 .big {
grid-column: span 2;
grid-row: span 2;
}
/* 4. 纯居中外壳 —— Grid 最短 */
.grid-4 {
display: grid;
place-items: center;
min-height: 100vh;
}
/* 5. 卡片内部 —— 底部按钮自动对齐 */
.grid-5 {
display: grid;
grid-template-rows: auto 1fr auto;
gap: 12px;
}
从浮动到 Flex,再到 Grid,CSS 布局的演进方向其实非常清晰:把过去需要靠技巧和 hack 表达的东西,变成语言内置的一等公民。Grid 的价值不只是“少写几行代码”,而是让布局意图能够被直接、准确地写在代码里,让三个月后接手的人一眼就能看懂。
希望这篇长文能帮你把 Grid 从“会用”推进到“用对、用巧、用稳”。如果条件允许,建议回到上面的两个交互实验室,亲手拖一拖、点一点——那比读十篇文章都管用。