0%

已完成 0/12 关

教学模式

你可以随时调整。

此设置调整教师的教学方式,无需重新生成课程。

第 5 关 含代码 含表格 约 17 分钟

样式与资源优化:局部作用域 CSS、CSS 变量、全局样式与图片处理

样式与资源优化:局部作用域 CSS、CSS 变量、全局样式与图片处理

在传统的网页制作中,写 CSS 常常是一场“噩梦”:你在页面 A 里给 h1 设置了紫色字体,结果页面 B、页面 C 甚至弹窗里的 h1 全都莫名其妙跟着变成了紫色;如果两个人协作,经常因为起了一样的类名(如 .title 或 .card)而互相覆盖样式,这被称为全局样式污染(Global CSS Pollution)。

Astro 带来了一个极其优雅现代的解决方案:开箱即用的局部作用域样式(Scoped CSS)!

今天,我们将为我们的站点增添色彩,搞清楚样式隔离的黑科技,并掌握现代网站必不可少的图片优化。


1. 核心概念与新术语白话解析

(1) 局部作用域样式(Scoped CSS)

  • 它是什么:你在 .astro 文件里写的 <style> 标签,其规则只对当前文件模板里的 HTML 元素生效,无论如何都不会泄漏到其他页面或组件中。
  • 底层原理(data-astro-cid-*): Astro 编译器在打包时,会自动在当前组件的 HTML 标签上打上一个唯一的随机指纹属性(例如 <h1 data-astro-cid-3tuxw7>关于我</h1>),同时把你的 CSS 选择器重写为 h1[data-astro-cid-3tuxw7]。 因此,就算你在十个不同文件里都写了 h1 { color: red; },它们各自也只会命中打着自己专属指纹的标签,物理上杜绝了互相干扰!

(2) define:vars 指令

  • 它是什么:Astro 专有的指令属性,写在 <style define:vars={{ myColor }}> 上。
  • 为什么需要它:前端开发者经常想根据数据动态改变外观(比如从文章属性里读取主色调)。define:vars 能把我们在顶部代码围栏里定义的 JavaScript 变量,直接变成合法的标准 CSS 自定义属性(CSS 变量 var(--myColor))。

(3) 全局样式(Global Styles)

  • 它是什么:作用于整站所有页面的通用基础样式(如重置边距 margin: 0、设定全站默认字体 font-family、页面背景底色等)。
  • 在哪能看到:通常放在 src/styles/global.css 中,并在页面组件的 frontmatter 脚本中通过 import '../styles/global.css' 导入。

(4) 累积布局偏移(CLS,Cumulative Layout Shift)与 <Image /> 组件

  • 它是什么:访问网页时,文字刚排好,一张巨大的图片突然慢吞吞加载出来,把下面的文字猛地往下挤了一下,导致你手滑点错了按钮——这种恼人的视觉跳动就叫 CLS 布局偏移。
  • Astro 官方解法:Astro 提供了内置的 <Image /> 组件,它会自动读取图片的真实宽高并预留占位空间,彻底消除 CLS,同时还会自动把体积庞大的 PNG/JPG 转换为体积只有原来几分之一的现代 WebP/AVIF 格式!

2. 动手实操:体验局部作用域与 CSS 变量

第一步:在 about.astro 中添加局部样式

打开 src/pages/about.astro,在 <head> 标签闭合前加入以下 <style> 块:

<html lang="zh-cn">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>{pageTitle}</title>
    <style>
      h1 {
        color: purple;
        font-size: 4rem;
      }
    </style>
  </head>

保存后切回浏览器,分别查看三个页面:

  • http://localhost:4321/about/:关于我大标题变成了巨大的紫色字体(4rem);
  • http://localhost:4321/(首页):标题依然是默认黑色!
  • http://localhost:4321/blog/(博客页):标题也依然是默认黑色!

动手验证:右键点击关于页面的大标题选择“检查(Inspect)”,在 Elements 标签页中观察,你会清晰地看到标签上被挂载了类似 data-astro-cid-xxxx 的属性标记。这就是 Scoped CSS 在默默守护你的样式隔离!

第二步:使用 define:vars 注入动态样式

现在我们想让技能列表展示出特定的高亮颜色与文字排版:

  1. 打开 src/pages/about.astro,在顶部的代码栅栏中增加三个样式变量:

    ---
    // ... 之前的数据 ...
    const skillColor = "navy";
    const fontWeight = "bold";
    const textCase = "uppercase";
    ---
  2. 在模板中给渲染技能的 <li> 标签挂上类名 class="skill":

    <ul>
      {skills.map((skill) => <li class="skill">{skill}</li>)}
    </ul>
  3. 将 <style> 标签升级为带有 define:vars 的动态样式:

    <style define:vars={{ skillColor, fontWeight, textCase }}>
      h1 {
        color: purple;
        font-size: 4rem;
      }
      .skill {
        color: var(--skillColor);
        font-weight: var(--fontWeight);
        text-transform: var(--textCase);
      }
    </style>

保存后刷新关于页面,你会发现技能列表瞬间变成了深蓝色(navy)、加粗,且全大写字母!你可以随意修改 frontmatter 里的 skillColor = "crimson",样式会立即同步响应。


3. 添加全站全局样式:global.css

现在各页面的背景和排版还是浏览器的原始白底黑字。让我们创建整站共享的全局样式规范:

第一步:创建全局样式文件

在 src/ 目录下新建 styles 文件夹,并在其中新建 global.css 文件(路径:src/styles/global.css),写入以下内容:

/* src/styles/global.css */
html {
  background-color: #f1f5f9;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  color: #1e293b;
}

body {
  margin: 0 auto;
  width: 100%;
  max-width: 80ch;
  padding: 1.5rem;
  line-height: 1.6;
}

* {
  box-sizing: border-box;
}

a {
  color: #00539f;
  margin-right: 1rem;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

第二步:在页面中导入全局样式

在 src/pages/index.astro、about.astro 和 blog.astro 三个文件的代码栅栏顶部,分别加入:

---
import '../styles/global.css';
// ... 其他变量定义 ...
---

保存后查看三个页面,你会发现所有页面的背景都变成了柔和清爽的浅灰色,内容居中排列,链接也有了精致的悬停效果!


4. 静态资源双轨制:图片该存放在哪里?

在 Astro 中处理图片有两种不同路径,切忌混淆:

存放路径引用方式Astro 是否处理适用场景
public/ 目录直接写绝对路径,如 <img src="/logo.png" />完全不处理,原样复制网站小图标 favicon.svg、站点清单 robots.txt、固定的第三方下载文件
src/assets/ 目录在 frontmatter 中 import 后,使用 <Image /> 组件全自动优化(压缩、转 WebP、注水宽高防 CLS)博客配图、封面横幅、高清摄影、产品展示图

官方推荐的最佳实践:使用 <Image /> 组件

Astro 内置了 astro:assets 模块。如果你在 src/assets/banner.png 存放了一张图片:

---
import { Image } from 'astro:assets';
import myBanner from '../assets/banner.png';
---

<!-- Astro 会自动算出图片的宽高,并在构建时自动压缩生成 WebP -->
<Image src={myBanner} alt="文章精彩封面横幅" width={800} />

5. 本关小结

  1. 局部样式开箱即用:<style> 默认自动打哈希隔离,页面间同名样式绝不互相打架。
  2. 数据连通样式:define:vars 让 frontmatter 的变量能够作为 CSS 变量使用。
  3. 全局与局部并存:global.css 定基调,组件内 <style> 细化微调。
  4. 图片优化首选 src/assets/:搭配 <Image /> 组件消除布局偏移并节省带宽。

在下一关中,我们将开始解决一个非常迫切的问题:既然导航链接和页脚在三个页面里都写了一模一样的内容,我们该如何将它们封装成可复用的独立组件呢?

下一站:可复用组件:抽离导航与页脚、组件间 Props 属性传递与组合 →
© 2026 Astro 入门与基础