Day-16-Grid网格布局

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-fillauto-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>&copy; 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;
}

✍️ 练习任务

基础练习

  1. 创建基础网格

    • 创建 3×3 的网格
    • 每个格子 100px,间距 10px
    • 给每个格子不同颜色
  2. 使用 fr 单位

    • 创建三列布局:1fr 2fr 1fr
    • 添加内容,观察比例变化
    • 调整浏览器窗口,验证响应式
  3. 跨越行列

    • 让第一个项目跨越 2 列
    • 让最后一个项目跨越 2 行
    • 使用 span 关键字实现

进阶练习

  1. 命名区域布局

    • 使用 grid-template-areas 创建页面布局
    • 包含:header、sidebar、main、footer
    • 实现响应式切换
  2. auto-fit 实践

    • 使用 auto-fitminmax() 创建自适应卡片网格
    • 最小宽度 200px
    • 测试不同屏幕尺寸
  3. 对齐方式

    • 尝试 justify-contentalign-content 的不同值
    • 使用 justify-selfalign-self 单独控制项目
    • 创建居中布局

实战挑战

  1. 照片画廊

    • 创建响应式照片网格
    • 悬停时放大图片
    • 使用 object-fit: cover 保持图片比例
  2. 仪表板布局

    • 顶部:标题栏
    • 左侧:导航菜单
    • 中间:主要内容区(2×2 网格)
    • 右侧:统计信息
    • 底部:状态栏
  3. 日历视图

    • 创建 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 动画,让你的网页动起来!