首页 / Astro 教程 / 组件级作用域样式与全局样式

Astro 教程

组件级作用域样式与全局样式

本教程共 56 篇 · 第 11 篇 · 更新于 2026-08-07 · 约 12 分钟阅读

AstroAstro 教程作用域样式全局样式is:globaldefine:varsCSS 层叠

本节目标:学会在 .astro 组件里写样式,理解“作用域样式”默认如何隔离、何时用 is:global 写全局样式,并掌握 define:vars、给子组件传 class 以及样式层叠顺序。

写样式在 Astro 里特别省心。你直接在组件里写 <style> 标签,Astro 就会自动接手处理 CSS,不用你额外配置。本章讲清楚两件事:样式默认怎么“圈地自萌”(作用域),以及什么时候要“打破圈地”变成全局。

组件里直接写 style 就行

给一个 Astro 组件加样式,简单到只需在模板里放一个 <style>

<style>
  h1 { color: red; }
</style>

就这么一句,Astro 会识别并自动处理这段 CSS。不需要引入额外的样式系统,原生 CSS 直接写。

作用域样式:默认隔离,不串门

Astro 里 <style> 的 CSS 默认是作用域(scoped)的。意思是:你写的样式只在“同一个组件内写的 HTML”上生效,Astro 在背后悄悄给这些元素加了专属标记,把样式圈在这一个组件里。

你写的是:

<style>
  h1 {
    color: red;
  }
  .text {
    color: blue;
  }
</style>

Astro 编译后实际变成(示意):

<style>
  h1[data-astro-cid-hhnqfkh6] {
     color: red;
  }
  .text[data-astro-cid-hhnqfkh6] {
    color: blue;
  }
</style>

那个 data-astro-cid-... 就是 Astro 自动加的“身份证”,保证样式只作用在本组件的元素上。

带来的好处很实在:

  • 不串味:作用域样式不会漏到网站其他地方。你在这个组件里写 h1 { color: red },别的页面标题不会跟着变红;
  • 可以放心用“低 specificity(特指度)”选择器:比如直接写 h1 {}p {} 这种笼统选择器也不用怕冲突,因为编译后都带上作用域了;
  • 不影响子组件:作用域样式不会套进你引入的其他 Astro 组件。想改子组件的样子,得把它包在一个 <div> 之类元素上,再去样式化那个外层。
Tip

初学者最常见的疑惑“我写的样式怎么没生效”,多半是作用域在作怪——你选中的元素其实在另一个组件里,不在当前作用域。记住:作用域样式只管“自己家”的 HTML。

全局样式:用 is:global 打破隔离

多数组件用作用域就够,但偶尔你确实需要“对整个网站生效”的样式,比如统一所有 <h1> 的颜色。这时给 <style>is:global 指令,退出自动作用域:

<style is:global>
  /* 不被作用域,原样发给浏览器。
     对全站所有 <h1> 生效。 */
  h1 { color: red; }
</style>
Note

is:global 里的样式“原样交付”,不再加作用域标记,所以会真正影响全局。全局样式要省着用,避免日后样式互相打架。

混着写::global() 局部 + 全局

有时你想要“局部为主、但其中一部分影响子内容”。Astro 支持在同一个 <style> 里用 :global() 选择器,只把某条规则变成全局,其余仍作用域。

<style>
  /* 只作用于本组件 */
  h1 { color: red; }

  /* 混合:只作用于本组件内的子 <h1> */
  article :global(h1) {
    color: blue;
  }
</style>

<h1>标题</h1>
<article><slot /></article>

这个模式常用来给“内容来自 Astro 之外”的区域上样式,比如博客正文、CMS 生成的文档——这些内容不在组件作用域里,得用 :global() 才能选中。

Warning

一个组件的外观如果“取决于有没有某个父组件包着”,以后排查样式会很难受。:global() 好用,但别滥用。

用 define:vars 从脚本传 CSS 变量

想让样式跟着脚本里的数据变?可以用 define:vars 指令,把 frontmatter 里的变量变成 CSS 变量传进 <style>

---
const foregroundColor = "rgb(221 243 228)";
const backgroundColor = "rgb(24 121 78)";
---

<style define:vars={{ foregroundColor, backgroundColor }}>
  h1 {
    background-color: var(--backgroundColor);
    color: var(--foregroundColor);
  }
</style>

<h1>你好</h1>

花括号里 { foregroundColor, backgroundColor } 是把这两个脚本变量映射到同名 CSS 变量。模板里就能用 var(--backgroundColor) 引用。这样改个变量,颜色就跟着变,不用改 CSS 本身。

给子组件传 class

在 Astro 里,class 这种 HTML 属性不会自动透传给子组件。想让父组件控制子组件根元素的 class,得在子组件里“显式接收”:

---
// 子组件 MyComponent.astro
const { class: className, ...rest } = Astro.props;
---

<div class={className} {...rest}>
  <slot/>
</div>

注意:class 是 JavaScript 保留字,解构时要改名(这里改成 className)。...rest 把其余属性一并传下去,保证 Astro 的作用域标记 data-astro-cid-* 也跟着过去。

父组件里就能这么用:

<style>
  .red { color: red; }
</style>

<MyComponent class="red">这行字会是红色!</MyComponent>
Note

如果你在配置里把 scopedStyleStrategy 改成了 'class''where',那 ...rest 这一步就不是必须的。默认策略下还是带上稳妥。

行内样式:style 属性

给单个元素加行内样式,直接用 style 属性。它可以是 CSS 字符串,也可以是“CSS 属性对象”:

// 两种写法等价:
<p style={{ color: "brown", textDecoration: "underline" }}>我的文字</p>
<p style="color: brown; text-decoration: underline;">我的文字</p>

对象写法在配合动态数据时更顺手(第 7 章讲过表达式)。

想用独立的 .css 文件,也有两种方式。

方式一:用 ESM import(推荐)。在 frontmatter 顶部 import:

---
// Astro 会自动打包并优化这段 CSS
// 也支持 .scss、.styl 等预处理器文件
import '../styles/utils.css';
---

<html><!-- 你的页面 --></html>

这种方式 Astro 会帮你打包、优化,还能处理来自 npm 包的样式表。

方式二:用 <link> 标签。指向 public/ 目录里或外部网站的 CSS,必须是绝对路径:

<head>
  <!-- 本地:/public/styles/global.css -->
  <link rel="stylesheet" href="/styles/global.css" />
  <!-- 外部 -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.24.1/themes/prism-tomorrow.css" />
</head>

<link> 会跳过 Astro 的打包优化。要优化就走 import 方式。

样式层叠顺序:谁压过谁

当一个元素同时被多来源 CSS 命中,浏览器先比“特指度(specificity)”,再比“出现顺序”——特指度高的赢;特指度一样时,后出现的值生效。

Astro 里不同来源的样式,按下面这个“出现顺序”评估(越往下优先级越高):

  1. <head> 里的 <link> 标签(优先级最低);
  2. import 进来的样式;
  3. 作用域样式(优先级最高)。

也就是说:作用域样式永远“最后出场”,同特指度下它会压过 import 和 link 的样式。但如果有条 import 的样式特指度更高(比如用了 #id),那它照样赢过作用域样式。

Tip

记不住层叠细节也没事,记住一句够用:作用域样式是“本地最强”,import 的 CSS 可能“漏”到别处。布局清晰、少用全局,样式就少踩坑。

作用域样式够不到动态插入的 HTML

有个细节容易踩坑:作用域样式只认「组件模板里写死的 HTML」。如果你用 set:html 把一段字符串当 HTML 塞进页面(比如从别处拿来的富文本),那些动态插进来的元素不在组件模板里,作用域标记也贴不到它们身上,于是 <style> 里的规则对它们无效。

---
const richText = "<p class='tip'>这段是运行时插进来的</p>";
---
<style>
  .tip { color: red; }  <!-- 对上面那段动态 HTML 不起作用 -->
</style>

<div set:html={richText} />

遇到这种情况,要么给那段内容加 :global() 选择器,要么用 is:global 写全局规则,要么在插入的 HTML 上直接带 style 属性或 class 配合全局样式表。一句话:作用域样式的「圈地」范围是编译时定死的,运行时塞进来的东西不在圈里。

Tip

调试样式时,打开浏览器开发者工具看元素,如果某个标签上带着 data-astro-cid- 属性,说明它受作用域样式管理;没有这个属性,就说明它逃出了作用域,得用 :global() 或全局样式去够它。

小结

Astro 样式的两大基调:组件内 <style> 默认作用域隔离,不串门;需要全站生效就用 is:global,或用 :global() 局部开个小口。define:vars 把脚本变量变成 CSS 变量,class 需显式透传给子组件,外部样式优先用 import。理解了“作用域 + 层叠顺序”,你的样式就不会再莫名其妙地互相打架。