Day 16 – CSS Grid 网格布局
🎯 学习目标
- 理解 Grid 布局的核心概念和优势
- 掌握 Grid 容器和项目的属性
- 学会创建二维布局系统(行和列)
- 能够实现复杂的网页布局
💡 核心概念
1. 什么是 Grid 布局?
Grid(网格)布局是 CSS 中最强大的二维布局系统:
┌─────────────────────────────────────┐
│ Flexbox vs Grid │
├─────────────────────────────────────┤
│ Flexbox:一维布局(行 或 列) │
│ ┌───┬───┬───┐ │
│ │ A │ B │ C │ ← 沿一个方向 │
│ └───┴───┴───┘ │
│ │
│ Grid:二维布局(行 和 列) │
│ ┌───┬───┬───┐ │
│ │ A │ B │ C │ ← 同时控制行和列 │
│ ├───┼───┼───┤ │
│ │ D │ E │ F │ │
│ └───┴───┴───┘ │
└─────────────────────────────────────┘
Grid 的优势:
- ✅ 同时控制行和列
- ✅ 强大的对齐能力
- ✅ 灵活的网格轨道
- ✅ 直观的布局代码
2. Grid 核心术语
容器(Container):
┌─────────────────────────────────────┐
│ ┌───┬───┬───┐ 项目(Items) │
│ │ A │ B │ C │ │
│ ├───┼───┼───┤ │
│ │ D │ E │ F │ │
│ └───┴───┴───┘ │
│ │
│ 网格线(Grid Lines) │
│ ↑ ↑ ↑ ↑ │
│ 1 2 3 4 (列线) │
│ │
│ 网格轨道(Grid Tracks) │
│ ←──────────→ │
│ 列轨道 / 行轨道 │
│ │
│ 网格区域(Grid Areas) │
│ ┌───────┐ │
│ │ A │ ← 1个或多个单元格 │
│ └───────┘ │
└─────────────────────────────────────┘
关键概念:
- 网格容器:应用
display: grid的父元素 - 网格项目:容器的直接子元素
- 网格线:划分网格的水平线和垂直线
- 网格轨道:两条相邻网格线之间的空间
- 网格单元格:最小的网格单位
- 网格区域:任意数量的网格单元格组成的矩形区域
📝 Grid 容器属性
1. 基础设置
/* 创建 Grid 容器 */
.container {
display: grid;
}
/* 创建行内 Grid 容器 */
.container {
display: inline-grid;
}
2. 定义列和行
固定尺寸:
.container {
display: grid;
/* 定义三列,每列 100px */
grid-template-columns: 100px 100px 100px;
/* 定义两行,每行 80px */
grid-template-rows: 80px 80px;
}
百分比:
.container {
grid-template-columns: 33.33% 33.33% 33.33%;
grid-template-rows: 50% 50%;
}
使用 fr 单位(fraction,分数单位):
.container {
/* 三列均分空间 */
grid-template-columns: 1fr 1fr 1fr;
/* 第一列 1 份,第二列 2 份 */
grid-template-columns: 1fr 2fr;
/* 混合使用 */
grid-template-columns: 200px 1fr 2fr;
}
repeat() 函数:
.container {
/* 等同于 1fr 1fr 1fr 1fr */
grid-template-columns: repeat(4, 1fr);
/* 复杂模式重复 */
grid-template-columns: repeat(2, 100px 1fr);
/* 结果:100px 1fr 100px 1fr */
}
auto-fill 和 auto-fit:
.container {
/* auto-fill:尽可能多地创建列 */
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
/* 结果:根据容器宽度自动填充列 */
/* auto-fit:与 auto-fill 类似,但会压缩空列 */
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
}
3. 间距(Gap)
.container {
/* 行间距 */
row-gap: 20px;
/* 列间距 */
column-gap: 30px;
/* 简写:行间距 列间距 */
gap: 20px 30px;
/* 行列相同 */
gap: 20px;
}
4. 命名网格线
.container {
grid-template-columns:
[start] 1fr
[middle] 2fr
[end];
grid-template-rows:
[header-start] 60px
[header-end main-start] 1fr
[main-end footer-start] 40px
[footer-end];
}
/* 使用命名线 */
.item {
grid-column: start / middle;
grid-row: header-start / header-end;
}
5. 命名网格区域
.container {
display: grid;
grid-template-columns: 200px 1fr 200px;
grid-template-rows: 60px 1fr 40px;
/* 布局模板 */
grid-template-areas:
"header header header"
"sidebar main ads"
"footer footer footer";
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.ads { grid-area: ads; }
.footer { grid-area: footer; }
可视化:
┌───────────────────────────────────┐
│ header │
├──────┬───────────────┬────────────┤
│ side │ main │ ads │
├──────┴───────────────┴────────────┤
│ footer │
└───────────────────────────────────┘
📝 Grid 项目属性
1. 指定项目位置
使用网格线编号:
.item {
/* 列:从第 1 条线到第 3 条线 */
grid-column: 1 / 3;
/* 行:从第 2 条线到第 4 条线 */
grid-row: 2 / 4;
}
使用 span 跨越:
.item {
/* 跨越 2 列 */
grid-column: span 2;
/* 从第 2 列开始,跨越 3 列 */
grid-column: 2 / span 3;
/* 从第 2 行开始,跨越到第 4 行 */
grid-row: 2 / 4;
}
简写:
.item {
/* grid-column-start / grid-column-end */
grid-column: 1 / 3;
/* grid-row-start / grid-row-end */
grid-row: 1 / 3;
/* 完全简写:row-start / column-start / row-end / column-end */
grid-area: 1 / 1 / 3 / 3;
}
使用命名:
.item {
grid-area: header; /* 使用 grid-template-areas 定义的名称 */
}
2. 对齐方式
单个项目:
.item {
/* 水平对齐(列方向) */
justify-self: start | end | center | stretch;
/* 垂直对齐(行方向) */
align-self: start | end | center | stretch;
/* 简写 */
place-self: align-self justify-self;
place-self: center center; /* 垂直居中 水平居中 */
}
所有项目(容器属性):
.container {
/* 所有项目水平对齐 */
justify-items: start | end | center | stretch;
/* 所有项目垂直对齐 */
align-items: start | end | center | stretch;
/* 简写 */
place-items: align-items justify-items;
}
3. 整个网格对齐
.container {
/* 网格在容器内的水平对齐 */
justify-content: start | end | center | stretch | space-around | space-between | space-evenly;
/* 网格在容器内的垂直对齐 */
align-content: start | end | center | stretch | space-around | space-between | space-evenly;
/* 简写 */
place-content: align-content justify-content;
}
对比图:
start: center: space-between:
┌────┐ ┌────┐ ┌────┐
│ AB │ │ AB │ │ A │
│ CD │ │ CD │ │ B │
└────┘ └────┘ │ CD │
│ E │
└────┘
🎮 实战示例
示例 1:基础网格布局
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Grid 基础布局</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(2, 150px);
gap: 20px;
max-width: 900px;
width: 100%;
}
.item {
background: white;
border-radius: 12px;
display: flex;
justify-content: center;
align-items: center;
font-size: 2rem;
font-weight: bold;
color: white;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
transition: transform 0.3s, box-shadow 0.3s;
cursor: pointer;
}
.item:hover {
transform: translateY(-5px);
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.3);
}
.item:nth-child(1) { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); }
.item:nth-child(2) { background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); }
.item:nth-child(3) { background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); }
.item:nth-child(4) { background: linear-gradient(135deg, #fa709a 0%, #fee140 100%); }
.item:nth-child(5) { background: linear-gradient(135deg, #30cfd0 0%, #330867 100%); }
.item:nth-child(6) { background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%); color: #333; }
.info {
position: fixed;
bottom: 20px;
right: 20px;
background: white;
padding: 15px 25px;
border-radius: 8px;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2);
font-size: 0.9rem;
}
</style>
</head>
<body>
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
</div>
<div class="info">
📐 3列 × 2行 | gap: 20px
</div>
<script>
// 点击项目显示位置信息
const items = document.querySelectorAll('.item');
const info = document.querySelector('.info');
items.forEach(item => {
item.addEventListener('click', function() {
const computedStyle = window.getComputedStyle(this);
const columnStart = computedStyle.gridColumnStart;
const columnEnd = computedStyle.gridColumnEnd;
const rowStart = computedStyle.gridRowStart;
const rowEnd = computedStyle.gridRowEnd;
info.innerHTML = `
📍 位置信息<br>
列: ${columnStart} → ${columnEnd}<br>
行: ${rowStart} → ${rowEnd}
`;
});
});
</script>
</body>
</html>
示例 2:响应式卡片网格
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>响应式卡片网格</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', sans-serif;
background: #f5f7fa;
padding: 40px 20px;
}
.header {
text-align: center;
margin-bottom: 40px;
}
.header h1 {
color: #2c3e50;
font-size: 2.5rem;
margin-bottom: 10px;
}
.header p {
color: #7f8c8d;
font-size: 1.1rem;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 25px;
max-width: 1200px;
margin: 0 auto;
}
.card {
background: white;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
transition: transform 0.3s, box-shadow 0.3s;
}
.card:hover {
transform: translateY(-8px);
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.15);
}
.card-image {
height: 180px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
justify-content: center;
align-items: center;
font-size: 4rem;
}
.card:nth-child(2) .card-image {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
.card:nth-child(3) .card-image {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
.card:nth-child(4) .card-image {
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
}
.card-content {
padding: 20px;
}
.card-title {
font-size: 1.3rem;
color: #2c3e50;
margin-bottom: 10px;
}
.card-description {
color: #7f8c8d;
line-height: 1.6;
margin-bottom: 15px;
}
.card-footer {
display: flex;
justify-content: space-between;
align-items: center;
}
.card-tag {
background: #ecf0f1;
padding: 5px 12px;
border-radius: 20px;
font-size: 0.85rem;
color: #7f8c8d;
}
.card-button {
background: #3498db;
color: white;
border: none;
padding: 8px 16px;
border-radius: 6px;
cursor: pointer;
font-size: 0.9rem;
transition: background 0.3s;
}
.card-button:hover {
background: #2980b9;
}
@media (max-width: 768px) {
.header h1 {
font-size: 2rem;
}
.grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="header">
<h1>响应式卡片网格</h1>
<p>自动适应屏幕尺寸的 Grid 布局</p>
</div>
<div class="grid">
<div class="card">
<div class="card-image">🚀</div>
<div class="card-content">
<h3 class="card-title">Grid 布局</h3>
<p class="card-description">
强大的二维布局系统,轻松实现复杂的网页布局。
</p>
<div class="card-footer">
<span class="card-tag">CSS</span>
<button class="card-button">了解更多</button>
</div>
</div>
</div>
<div class="card">
<div class="card-image">🎨</div>
<div class="card-content">
<h3 class="card-title">响应式设计</h3>
<p class="card-description">
使用 auto-fit 和 minmax 创建自适应网格布局。
</p>
<div class="card-footer">
<span class="card-tag">设计</span>
<button class="card-button">了解更多</button>
</div>
</div>
</div>
<div class="card">
<div class="card-image">⚡</div>
<div class="card-content">
<h3 class="card-title">性能优化</h3>
<p class="card-description">
Grid 布局性能优秀,渲染效率高,适合复杂布局。
</p>
<div class="card-footer">
<span class="card-tag">优化</span>
<button class="card-button">了解更多</button>
</div>
</div>
</div>
<div class="card">
<div class="card-image">📱</div>
<div class="card-content">
<h3 class="card-title">移动优先</h3>
<p class="card-description">
完美支持移动设备,提供流畅的用户体验。
</p>
<div class="card-footer">
<span class="card-tag">移动端</span>
<button class="card-button">了解更多</button>
</div>
</div>
</div>
</div>
<script>
// 按钮点击事件
document.querySelectorAll('.card-button').forEach(button => {
button.addEventListener('click', function() {
const card = this.closest('.card');
const title = card.querySelector('.card-title').textContent;
alert(`你点击了:${title}`);
});
});
</script>
</body>
</html>
示例 3:圣杯布局(Holy Grail Layout)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>圣杯布局 - Grid</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', sans-serif;
min-height: 100vh;
display: grid;
grid-template-areas:
"header header header"
"nav main ads"
"footer footer footer";
grid-template-rows: auto 1fr auto;
grid-template-columns: 200px 1fr 200px;
gap: 0;
}
.header {
grid-area: header;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.header h1 {
font-size: 1.8rem;
}
.header nav ul {
display: flex;
list-style: none;
gap: 20px;
}
.header nav a {
color: white;
text-decoration: none;
transition: opacity 0.3s;
}
.header nav a:hover {
opacity: 0.8;
}
.nav {
grid-area: nav;
background: #2c3e50;
color: white;
padding: 20px;
}
.nav h3 {
margin-bottom: 15px;
color: #ecf0f1;
}
.nav ul {
list-style: none;
}
.nav li {
padding: 10px 0;
border-bottom: 1px solid #34495e;
}
.nav a {
color: #bdc3c7;
text-decoration: none;
transition: color 0.3s;
}
.nav a:hover {
color: white;
}
.main {
grid-area: main;
padding: 30px;
background: #ecf0f1;
}
.main h2 {
color: #2c3e50;
margin-bottom: 15px;
font-size: 1.8rem;
}
.main p {
color: #7f8c8d;
line-height: 1.8;
margin-bottom: 15px;
}
.ads {
grid-area: ads;
background: #34495e;
color: white;
padding: 20px;
}
.ads h3 {
margin-bottom: 15px;
}
.ad-item {
background: #2c3e50;
padding: 15px;
margin-bottom: 15px;
border-radius: 8px;
}
.footer {
grid-area: footer;
background: #1a252f;
color: #bdc3c7;
padding: 20px;
text-align: center;
}
/* 响应式设计 */
@media (max-width: 900px) {
body {
grid-template-areas:
"header header"
"nav main"
"ads ads"
"footer footer";
grid-template-columns: 150px 1fr;
}
}
@media (max-width: 600px) {
body {
grid-template-areas:
"header"
"nav"
"main"
"ads"
"footer";
grid-template-columns: 1fr;
}
.header {
flex-direction: column;
gap: 15px;
}
.header nav ul {
flex-direction: column;
text-align: center;
}
}
</style>
</head>
<body>
<header class="header">
<h1>🎨 Grid 圣杯布局</h1>
<nav>
<ul>
<li><a href="#">首页</a></li>
<li><a href="#">关于</a></li>
<li><a href="#">联系</a></li>
</ul>
</nav>
</header>
<aside class="nav">
<h3>📚 导航</h3>
<ul>
<li><a href="#">HTML 基础</a></li>
<li><a href="#">CSS 样式</a></li>
<li><a href="#">JavaScript</a></li>
<li><a href="#">响应式设计</a></li>
<li><a href="#">性能优化</a></li>
</ul>
</aside>
<main class="main">
<h2>什么是圣杯布局?</h2>
<p>
圣杯布局是网页设计中经典的布局模式,包含页眉、页脚、
主内容区、侧边栏和广告区。使用 Grid 可以轻松实现这种布局。
</p>
<h2>Grid 的优势</h2>
<p>
与传统的浮动和定位相比,Grid 提供了更直观、更强大的布局方式。
你可以精确控制每个区域的位置和大小,而无需复杂的计算。
</p>
<h2>响应式设计</h3>
<p>
通过媒体查询和 Grid 的灵活性,我们可以轻松创建适应不同屏幕尺寸的布局。
试试调整浏览器窗口大小,看看布局如何变化!
</p>
</main>
<aside class="ads">
<h3>📢 广告</h3>
<div class="ad-item">
<h4>推荐课程</h4>
<p>学习 Grid 布局,提升前端技能!</p>
</div>
<div class="ad-item">
<h4>最新文章</h4>
<p>CSS Grid 完全指南</p>
</div>
</aside>
<footer class="footer">
<p>© 2024 Grid 布局示例 | 使用 CSS Grid 构建</p>
</footer>
</body>
</html>
⚠️ 重要注意事项
1. 浏览器兼容性
现代浏览器支持:
- ✅ Chrome 57+
- ✅ Firefox 52+
- ✅ Safari 10.1+
- ✅ Edge 16+
检查兼容性:
/* 降级处理 */
.container {
display: flex; /* 降级方案 */
flex-wrap: wrap;
}
@supports (display: grid) {
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
}
2. Grid vs Flexbox 选择
选择 Flexbox 当:
✅ 一维布局(行或列)
✅ 内容驱动布局
✅ 简单的对齐需求
选择 Grid 当:
✅ 二维布局(行和列同时控制)
✅ 布局驱动内容
✅ 复杂的页面结构
3. 性能考虑
Grid 性能优秀,但要注意:
- 避免过度嵌套 Grid 容器
- 大型网格(100+ 项目)可能影响性能
- 使用
contain属性优化渲染
.container {
contain: layout style paint; /* 隔离渲染 */
}
4. 调试技巧
可视化网格:
.container {
/* 显示网格线(开发工具) */
/* 在浏览器 DevTools 中启用 */
}
/* 使用 outline 调试 */
.item {
outline: 2px solid red;
}
✍️ 练习任务
基础练习
-
创建基础网格
- 创建 3×3 的网格
- 每个格子 100px,间距 10px
- 给每个格子不同颜色
-
使用 fr 单位
- 创建三列布局:1fr 2fr 1fr
- 添加内容,观察比例变化
- 调整浏览器窗口,验证响应式
-
跨越行列
- 让第一个项目跨越 2 列
- 让最后一个项目跨越 2 行
- 使用
span关键字实现
进阶练习
-
命名区域布局
- 使用
grid-template-areas创建页面布局 - 包含:header、sidebar、main、footer
- 实现响应式切换
- 使用
-
auto-fit 实践
- 使用
auto-fit和minmax()创建自适应卡片网格 - 最小宽度 200px
- 测试不同屏幕尺寸
- 使用
-
对齐方式
- 尝试
justify-content和align-content的不同值 - 使用
justify-self和align-self单独控制项目 - 创建居中布局
- 尝试
实战挑战
-
照片画廊
- 创建响应式照片网格
- 悬停时放大图片
- 使用
object-fit: cover保持图片比例
-
仪表板布局
- 顶部:标题栏
- 左侧:导航菜单
- 中间:主要内容区(2×2 网格)
- 右侧:统计信息
- 底部:状态栏
-
日历视图
- 创建 7 列网格(一周七天)
- 6 行(一个月约 6 周)
- 今天的日期高亮显示
- 响应式设计(移动端单列)
💡 常见问题 FAQ
Q1: Grid 和 Flexbox 可以混用吗?
答:可以!这是最佳实践之一。
/* Grid 用于整体布局 */
.page {
display: grid;
grid-template-columns: 250px 1fr;
}
/* Flexbox 用于组件内部 */
.nav {
display: flex;
flex-direction: column;
}
Q2: 如何让 Grid 项目自适应内容大小?
答:使用 auto 关键字。
.container {
grid-template-columns: auto 1fr auto;
/* 左右列根据内容宽度,中间列占剩余空间 */
}
Q3: Grid 可以实现瀑布流布局吗?
答:原生 Grid 不支持瀑布流,但可以用 CSS Columns 或 JavaScript 库(如 Masonry.js)。
/* 使用 CSS Columns 替代 */
.waterfall {
column-count: 3;
column-gap: 20px;
}
Q4: 为什么我的 Grid 布局在移动端乱了?
答:检查以下几点:
- 是否设置了适当的
minmax()值 - 是否添加了媒体查询
- 容器宽度是否受限
@media (max-width: 768px) {
.grid {
grid-template-columns: 1fr; /* 单列布局 */
}
}
Q5: 如何创建等高列?
答:Grid 默认就支持等高列!
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
/* 所有列自动等高 */
}
📚 拓展阅读
推荐资源:
相关主题:
- Day 15: Flexbox 布局
- Day 18: 响应式设计(更深入学习媒体查询)
- Day 13: CSS 基础(盒模型和定位)
下一课预告:Day 17 – CSS 动画,让你的网页动起来!