Flex 的局限
Flex 是一维布局的好手,但如果遇到需要同时控制行和列的场景,比如整个页面的骨架布局、相册网格,Flex 就有点力不从心了。
想象一下:Flex 像排队买奶茶,一条队伍从头排到尾。而 Grid 像在棋盘上摆棋子,每个棋子可以精确落在第几行第几列。
这就是 Grid 的核心能力——二维布局。
Grid 容器与 Grid Item
给父元素加 display: grid,它就变成了网格容器,直接子元素自动成为网格项。
<!-- CSS 原生写法 -->
<div class="grid-container">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<div class="grid-item">4</div>
</div>
<style>
.grid-container {
display: grid;
}
</style>
<!-- Tailwind 写法 -->
<div class="grid">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
</div>
grid-template-columns:定义列
这是 Grid 最常用的属性,用来定义每一列的宽度。
| CSS | Tailwind | 说明 |
|---|---|---|
grid-template-columns: 100px 100px 100px | grid-cols-3 | 3 列,每列 100px |
grid-template-columns: 1fr 2fr 1fr | grid-cols-3 + 自定义 | 中间列占 50%,两边各 25% |
grid-template-columns: repeat(3, 1fr) | grid-cols-3 | 3 列平分空间 |
<!-- Tailwind 示例:3 列网格 -->
<div class="grid grid-cols-3 gap-4">
<div class="rounded bg-red-200 p-4">1</div>
<div class="rounded bg-blue-200 p-4">2</div>
<div class="rounded bg-green-200 p-4">3</div>
<div class="rounded bg-yellow-200 p-4">4</div>
<div class="rounded bg-purple-200 p-4">5</div>
<div class="rounded bg-pink-200 p-4">6</div>
</div>
fr 单位:比例份额
fr 是 Grid 特有的单位,表示”比例份额”。记住它的核心特点:它会考虑 gap 的影响。
/* 把容器分成 4 份:左边 1 份,中间 2 份,右边 1 份 */
grid-template-columns: 1fr 2fr 1fr;
fr vs 百分比:
| 写法 | 效果 | 与 gap 的关系 |
|---|---|---|
1fr 1fr 1fr | 三列均分(各 1/3) | 考虑 gap,调整后更准确 |
33.33% 33.33% 33.33% | 三列均分(各 1/3) | 不考虑 gap,可能溢出 |
<!-- Tailwind 示例:左右固定宽度,中间自适应 -->
<div class="grid grid-cols-3 gap-4">
<div class="col-span-1 bg-red-200 p-4">侧边栏 200px</div>
<div class="col-span-1 bg-blue-200 p-4">中间内容 flex-1</div>
<div class="col-span-1 bg-green-200 p-4">侧边栏 200px</div>
</div>
repeat() 函数:简化重复
如果有多列相同宽度,用 repeat() 可以少写很多代码。
/* 等价于 1fr 1fr 1fr 1fr 1fr */
grid-template-columns: repeat(5, 1fr);
/* 等价于 200px 200px 200px */
grid-template-columns: repeat(3, 200px);
minmax():最小最大范围
minmax(min, max) 设置一个范围,让网格项在这个范围内自适应。
/* 每列最小 200px,最大 1fr(尽可能宽) */
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
记住这个组合的用法:repeat(auto-fit, minmax(200px, 1fr)) 是最常用的响应式写法,等价于 Tailwind 的 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 系列,但不需要写断点。
<!-- Tailwind 示例:自动响应式网格,无需媒体查询 -->
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
<div class="rounded bg-red-200 p-4">卡片1</div>
<div class="rounded bg-blue-200 p-4">卡片2</div>
<div class="rounded bg-green-200 p-4">卡片3</div>
<div class="rounded bg-yellow-200 p-4">卡片4</div>
<div class="rounded bg-purple-200 p-4">卡片5</div>
</div>
gap:网格间距
控制网格项之间的间距,不作用于容器边缘。
| CSS | Tailwind | 说明 |
|---|---|---|
gap: 16px | gap-4 | 行列间距相同 |
row-gap: 16px | gap-y-4 | 仅行间距 |
column-gap: 16px | gap-x-4 | 仅列间距 |
grid-column / grid-row:跨列跨行
让一个网格项占据多个列或行。
| CSS | Tailwind | 说明 |
|---|---|---|
grid-column: span 2 | col-span-2 | 跨 2 列 |
grid-column: span 3 | col-span-3 | 跨 3 列 |
grid-row: span 2 | row-span-2 | 跨 2 行 |
grid-column: 1 / 3 | col-start-1 col-end-3 | 从第 1 列开始,第 3 列结束 |
<!-- Tailwind 示例:让第一个元素跨两列 -->
<div class="grid grid-cols-3 gap-4">
<div class="col-span-2 row-span-2 rounded bg-red-200 p-4">
大卡片(跨2列2行)
</div>
<div class="rounded bg-blue-200 p-4">小卡片</div>
<div class="rounded bg-green-200 p-4">小卡片</div>
<div class="rounded bg-yellow-200 p-4">小卡片</div>
</div>
实战:页面骨架布局
用 Grid 构建经典的后台管理系统骨架:
<!-- Tailwind:页面骨架布局 -->
<div class="grid h-screen grid-cols-4 grid-rows-5 gap-0">
<!-- Header -->
<header class="col-span-4 flex items-center bg-gray-800 p-4 text-white">
<div class="text-lg font-bold">管理系统</div>
<div class="ml-auto flex gap-4">
<span>用户</span>
<span>设置</span>
</div>
</header>
<!-- Sidebar -->
<aside class="col-span-1 row-span-4 bg-gray-100 p-4">
<nav class="flex flex-col gap-2">
<a href="#" class="rounded p-2 hover:bg-gray-200">仪表盘</a>
<a href="#" class="rounded p-2 hover:bg-gray-200">用户管理</a>
<a href="#" class="rounded p-2 hover:bg-gray-200">订单管理</a>
<a href="#" class="rounded p-2 hover:bg-gray-200">数据分析</a>
</nav>
</aside>
<!-- Main Content -->
<main class="col-span-3 row-span-4 overflow-auto p-6">
<h1 class="mb-6 text-2xl font-bold">欢迎回来</h1>
<div class="mb-6 grid grid-cols-3 gap-4">
<div class="rounded bg-white p-4 shadow">数据卡片1</div>
<div class="rounded bg-white p-4 shadow">数据卡片2</div>
<div class="rounded bg-white p-4 shadow">数据卡片3</div>
</div>
<div class="rounded bg-white p-4 shadow">
<p>主要内容区域</p>
</div>
</main>
<!-- Footer -->
<footer class="col-span-4 bg-gray-100 p-4 text-center text-sm text-gray-600">
2024 系统管理后台
</footer>
</div>
Flex vs Grid:什么时候用哪个
这是最重要的选择问题,记住下面的原则:
| 场景 | Flex | Grid | 原因 |
|---|---|---|---|
| 导航栏 | 适合 | 可以 | 水平排列,flex 更简洁 |
| 卡片列表(单行) | 适合 | 可以 | 一维排列,flex 足够 |
| 卡片网格(多行多列) | 勉强 | 必选 | 二维布局,grid 更强 |
| 整个页面骨架 | 勉强 | 必选 | 同时控制行列 |
| 垂直居中 | 必选 | 可以 | flex 更直观 |
| 组件内部布局 | 必选 | 可以 | flex 够用,grid 杀鸡用牛刀 |
记住这个核心区别:
- Flex = 一维分布,适合”排队”场景
- Grid = 二维定位,适合”摆棋”场景
auto-fit vs auto-fill:重要区别
这两个函数很容易混淆,记住它们的区别:
/* auto-fit:列数自适应,空白空间被已有列瓜分 */
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
/* auto-fill:列数固定(能放多少放多少),空白空间保留 */
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
实际效果:
- 容器宽度 800px,每列最小 200px
auto-fit:4 列(200px × 4),每列实际 200pxauto-fill:4 列(200px × 4),但会留出空白- 当只有 1 个元素时:
auto-fit的列会伸展占满,auto-fill的列保持最小宽度
记住:大多数情况下用 auto-fit,它更符合”自适应”的期望。
常见踩坑与避坑指南
踩坑 1:fr 和百分比混淆
1fr 和 100% 看起来相似,但 fr 会考虑 gap 的影响。如果你用了 gap,用 fr 更准确。
/* 用 gap 时,fr 更准确 */
grid-template-columns: 1fr 1fr;
gap: 20px;
/* 百分比可能出问题:100% + 20px gap = 溢出 */
grid-template-columns: 50% 50%;
gap: 20px;
踩坑 2:隐式网格与显式网格
如果网格项多于你定义的网格数量,Grid 会自动创建隐式网格。如果不控制它,可能会出现意外布局。
/* 控制隐式网格的行高 */
grid-auto-rows: minmax(100px, auto);
踩坑 3:没有设置 grid-template 直接用 span
如果你没有定义 grid-template-columns,直接用 col-span-* 会让元素跨所有隐式列,可能不是你要的效果。先定义网格,再使用跨列。
速查表
| 功能 | CSS 原生 | Tailwind |
|---|---|---|
| 启用 grid | display: grid | grid |
| 定义列数 | grid-template-columns: repeat(3, 1fr) | grid-cols-3 |
| 定义行数 | grid-template-rows: repeat(3, 1fr) | grid-rows-3 |
| 跨列 | grid-column: span 2 | col-span-2 |
| 跨行 | grid-row: span 2 | row-span-2 |
| 间距 | gap: 16px | gap-4 |
| 响应式列 | repeat(auto-fit, minmax(200px, 1fr)) | grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 |
| 列起始位置 | grid-column-start: 2 | col-start-2 |
| 列结束位置 | grid-column-end: 4 | col-end-4 |
记住 Grid 的核心:Grid 是二维布局工具,适合同时控制行和列的场景。Flex 适合组件内的一维排列,Grid 适合页面级别的整体布局。配合使用,才能构建完整的现代页面。