在官方文档中,写的还是非常详细的,我将通过官方文档,简单的了解一下Astro

官方文档地址:

https://docs.astro.build/zh-cn/basics/astro-components/#html-%E7%BB%84%E4%BB%B6

官方文档里面是 组件 、布局、 样式(CSS)、字体,还有引用一些npm包的教程,简单总结一下

组件

组件就是Astro构建的基础模块,纯html 无需客户端运行时的模板组件。

文件后缀名是.astro

目录 /src

整体的使用感受和cms那种模块化模板很像,代码风格和TS很像

格式

---
// 组件脚本(JavaScript)
---
<!-- 组件模板(HTML + JS 表达式)-->

组件脚本

Astro 使用代码围栏(---)来识别 Astro 组件中的组件脚本。

如果你以前写过 Markdown,你可能已经熟悉了叫做 frontmatter 的类似概念。

Astro 的组件脚本的想法直接受到了这个概念的启发。

它的代码围栏的主要目的就是保证你编写或引用的js被‘’围起来‘’,

它并不会直接在前端展示原始代码,有点类似于常见的cms的标签引用的逻辑,只是获取数据,然后渲染到前端。

脚本可以引用的类型:

  • 导入其他 Astro 组件

  • 导入其他框架组件,如 React

  • 导入数据,如 JSON 文件

  • 从 API 或数据库中获取内容

  • 创建你要在模板中引用的变量

---
import SomeAstroComponent from '../components/SomeAstroComponent.astro';

import SomeReactComponent from '../components/SomeReactComponent.jsx';

import someData from '../data/pokemon.json';

// 访问传入的组件参数,如 `<X title="Hello, World"/>`
const { title } = Astro.props;

// 获取外部数据,甚至可以从私有 API 和数据库中获取
const data = await fetch ('SOME_SECRET_API_URL/users').then (r => r.json ());
---
<!-- 你的模板在这! -->

组件模板

组件模板决定了组件最终输出的 HTML。

它不仅能写普通的 HTML,还能结合脚本(--- 部分)中的数据和 JavaScript 表达式,来动态生成更复杂的 HTML 内容。

---
// 组件脚本: 定义逻辑与数据 (仅在服务器运行)

// 导入其他组件
import Banner from '../components/Banner.astro';
import Avatar from '../components/Avatar.astro';
import ReactPokemonComponent from '../components/ReactPokemonComponent.jsx';

// 定义数据
const myFavoritePokemon = [
  { id: 1, name: '皮卡丘' },
  { id: 2, name: '小火龙' },
  { id: 3, name: '妙蛙种子' }
];

// 接收 Props (可设置默认值)
const { title = "组件默认标题" } = Astro.props;
---

<!-- 组件模板: 定义 HTML 结构 -->

<!-- 直接渲染导入的组件 -->
<Banner />

<!-- 使用 {} 渲染变量 -->
<h1>{title}</h1>

<!-- .map() 遍历数组,生成列表 -->
<ul>
  {myFavoritePokemon.map(pokemon => (
    <li>{pokemon.name}</li>
  ))}
</ul>

<!-- `client:visible`: 组件可见时加载 JS,实现交互 -->
<ReactPokemonComponent client:visible />

<!-- `server:defer`: 延迟渲染,不阻塞页面加载 -->
<Avatar server:defer>
  <!-- `slot="fallback"`: 组件加载完成前显示的占位符 -->
  <div slot="fallback">正在加载...</div>
</Avatar>

<!-- `class:list`: 动态组合 CSS 类 -->
<!-- 生成 class="card featured highlight" -->
<div class:list={[ "card", { featured: true }, "highlight" ]} />

基于组件的设计

官方说的有点含糊

个人理解:

  • 头部(header)页脚(footer)侧边栏 等每个界面几乎都复用的组件

    • 有点类似CMS中模板(主题)的写法

  • 组件库类型

    • 比如构建一个组件库模板,将按钮、徽章 各类组件单独写在单独文件中的写法

组件参数

Astro 组件可以定义和接受参数。

这些参数可用于组件模板以呈现 HTML。可以在 frontmatter script 中的 Astro.props 中使用。

这一块个人感觉非常像TypeScript/React 组件,

下面我用一个案例展示

可以清晰的看出,组件为一个div,然后有一些样式,在主界面 通过组件参数方式 ,填写对应的参数值即可。

---
//  src\pages\simple.astro
// 简单演示页面
import Layout from '../layouts/Layout.astro';
import SimpleCard from '../components/SimpleCard.astro';
---

<Layout title="简单组件参数演示">
  <main>
    <h1>Astro 组件参数简单演示</h1>
    
    <!-- 直接传递字符串参数 -->
    <SimpleCard 
      title="欢迎使用 Astro" 
      content="这是一个简单的卡片组件示例。" 
    />
    
    <!-- 使用变量传递参数 -->
    <SimpleCard 
      title="动态内容" 
      content="这个内容来自变量传递。" 
    />
    
    <!-- 传递不同的内容 -->
    <SimpleCard 
      title="学习要点" 
      content="组件通过 Astro.props 接收参数,然后在模板中使用花括号 {} 显示。" 
    />
  </main>
</Layout>

<style>
  main {
    max-width: 800px;
    margin: 0 auto;
    padding: 20px;
  }
  
  h1 {
    text-align: center;
    color: #333;
    margin-bottom: 30px;
  }
</style> 
---
//  src\components\SimpleCard.astro
// 简单卡片组件 - 接受标题和内容参数
// 使用方式:<SimpleCard title="我的标题" content="这是内容" />
const { title, content } = Astro.props;
---

<div class="card">
  <h2>{title}</h2>
  <p>{content}</p>
</div>

<style>
  .card {
    border: 1px solid #ddd;
    border-radius: 8px;
    padding: 20px;
    margin: 10px;
    background: white;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  }
  
  h2 {
    color: #333;
    margin-top: 0;
  }
  
  p {
    color: #666;
    line-height: 1.5;
  }
</style> 

插槽

<slot /> 元素是嵌入外部 HTML 内容的占位符,你可以将其他文件中的子元素注入(或“嵌入”)到组件模板中。

默认情况下,传递给组件的所有子元素都将呈现在 <slot /> 中。

Slot 就像是组件中的"空位",可以让你往里面放内容。

这个功能和有后端的CMS的标签实现方式的逻辑是不是很像?

想象一下:

  • 组件 = 一个相框

  • Slot = 相框中间的空位

  • 你可以往空位里放任何图片(内容)

举一个简单的案例:

创建一个有"空位"的组件

<!-- Box.astro -->
<div class="box">
  <slot />  <!-- 这就是"空位" -->
</div>

使用组件时往"空位"里放内容

<Box>
  <p>我是放进去的内容</p>  <!-- 这些内容会出现在 slot 的位置 -->
</Box>

最终效果

<div class="box">
  <p>我是放进去的内容</p>
</div>

命名插槽 = 多个"空位"

<!-- Card.astro -->
<div class="card">
  <div class="header">
    <slot name="title" />  <!-- 标题空位 -->
  </div>
  <div class="body">
    <slot />  <!-- 内容空位(默认) -->
  </div>
</div>
<Card>
  <h2 slot="title">我的标题</h2>  <!-- 放到 title 空位 -->
  <p>我的内容</p>  <!-- 放到默认空位 -->
</Card>

布局

布局是可重用的页面模板,提供统一的页面结构和样式。

这个就是组件组成的布局,为可重复用的模板。

比如:标题,导航栏,页脚这种组件

基础布局

---
// 使用基础布局的页面
import SimpleLayout from '../layouts/SimpleLayout.astro';
---

<SimpleLayout title="布局演示页面">
  <p>这是页面的主要内容。</p>
  <p>它被包裹在布局组件中,自动获得了:</p>
  <ul>
    <li>统一的页面结构</li>
    <li>导航栏</li>
    <li>页面标题</li>
    <li>底部版权信息</li>
  </ul>
  
  <h2>布局的好处</h2>
  <p>使用布局可以让所有页面保持一致的外观和结构。</p>
</SimpleLayout> 
---
// 基础布局组件
const { title } = Astro.props;
---

<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{title}</title>
  </head>
  <body>
    <!-- 页面头部 -->
    <header>
      <nav>
        <a href="/">首页</a>
        <a href="/about">关于</a>
        <a href="/contact">联系</a>
      </nav>
    </header>
    
    <!-- 主要内容区域 -->
    <main>
      <h1>{title}</h1>
      <slot /> <!-- 页面内容插入这里 -->
    </main>
    
    <!-- 页面底部 -->
    <footer>
      <p>© 2024 我的网站</p>
    </footer>
  </body>
</html>

<style>
  body {
    font-family: Arial, sans-serif;
    margin: 0;
    padding: 0;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
  }
  
  header {
    background: #333;
    color: white;
    padding: 1rem;
  }
  
  nav a {
    color: white;
    text-decoration: none;
    margin-right: 1rem;
  }
  
  nav a:hover {
    text-decoration: underline;
  }
  
  main {
    flex: 1;
    padding: 2rem;
    max-width: 800px;
    margin: 0 auto;
  }
  
  footer {
    background: #f5f5f5;
    text-align: center;
    padding: 1rem;
    margin-top: auto;
  }
</style> 

效果图:

Markdown 布局

可以使用md格式去渲染

---
layout: ../layouts/BlogLayout.astro
title: "我的第一篇博客"
author: "张三"
date: "2024年1月15日"
tags: ["技术", "学习", "Astro"]
---

## 欢迎来到我的博客

这是我的第一篇博客文章,使用 Astro 的 Markdown 布局功能。

### 什么是 Markdown 布局?

Markdown 布局让我可以:

- 专注于写内容,不用担心页面结构
- 自动获得统一的文章样式
- 通过 frontmatter 传递文章信息

### 代码示例

```javascript
console.log("Hello, Astro!");
```

### 总结

使用布局让写博客变得更简单! 
---
// 博客文章布局
const { frontmatter } = Astro.props;
---

<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{frontmatter.title}</title>
  </head>
  <body>
    <header>
      <a href="/">← 返回首页</a>
    </header>
    
    <article>
      <!-- 文章标题 -->
      <h1>{frontmatter.title}</h1>
      
      <!-- 文章信息 -->
      <div class="meta">
        <p>作者:{frontmatter.author}</p>
        <p>发布时间:{frontmatter.date}</p>
        {frontmatter.tags && (
          <p>标签:{frontmatter.tags.join(', ')}</p>
        )}
      </div>
      
      <!-- 文章内容 -->
      <div class="content">
        <slot /> <!-- Markdown 内容插入这里 -->
      </div>
    </article>
  </body>
</html>

<style>
  body {
    font-family: Arial, sans-serif;
    max-width: 800px;
    margin: 0 auto;
    padding: 2rem;
    line-height: 1.6;
  }
  
  header {
    margin-bottom: 2rem;
  }
  
  header a {
    color: #666;
    text-decoration: none;
  }
  
  header a:hover {
    text-decoration: underline;
  }
  
  h1 {
    color: #333;
    border-bottom: 2px solid #eee;
    padding-bottom: 0.5rem;
  }
  
  .meta {
    background: #f9f9f9;
    padding: 1rem;
    border-radius: 5px;
    margin: 1rem 0;
  }
  
  .meta p {
    margin: 0.5rem 0;
    color: #666;
  }
  
  .content {
    margin-top: 2rem;
  }
</style> 

效果图:

嵌套布局

嵌套结构:

BaseLayout (框架)

└── ArticleLayout (文章)

└── 页面内容

---
// 文章布局 - 嵌套在基础布局中
import BaseLayout from './BaseLayout.astro';
const { title, author, date } = Astro.props;
---

<BaseLayout title={title}>
  <main class="article-container">
    <!-- 文章头部 -->
    <header class="article-header">
      <h1>{title}</h1>
      <div class="article-meta">
        <span>作者:{author}</span>
        <span>发布:{date}</span>
      </div>
    </header>
    
    <!-- 文章内容 -->
    <article class="article-content">
      <slot />
    </article>
    
    <!-- 文章底部 -->
    <div class="article-footer">
      <p>感谢阅读!</p>
    </div>
  </main>
</BaseLayout>

<style>
  .article-container {
    max-width: 800px;
    margin: 0 auto;
    padding: 2rem;
    flex: 1;
  }
  
  .article-header {
    border-bottom: 3px solid #3498db;
    padding-bottom: 1rem;
    margin-bottom: 2rem;
  }
  
  .article-header h1 {
    color: #2c3e50;
    margin: 0 0 1rem 0;
    font-size: 2.5rem;
  }
  
  .article-meta {
    color: #7f8c8d;
  }
  
  .article-meta span {
    margin-right: 2rem;
  }
  
  .article-content {
    line-height: 1.8;
    font-size: 1.1rem;
  }
  
  .article-footer {
    margin-top: 3rem;
    padding-top: 2rem;
    border-top: 1px solid #ecf0f1;
    text-align: center;
    color: #95a5a6;
  }
</style> 
---
// 基础布局 - 提供页面框架
const { title } = Astro.props;
---

<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{title}</title>
  </head>
  <body>
    <!-- 全站导航 -->
    <nav class="main-nav">
      <a href="/">首页</a>
      <a href="/blog">博客</a>
      <a href="/about">关于</a>
    </nav>
    
    <!-- 内容区域 -->
    <slot />
    
    <!-- 全站底部 -->
    <footer>
      <p>© 2024 我的网站 - 所有权利保留</p>
    </footer>
  </body>
</html>

<style>
  body {
    font-family: Arial, sans-serif;
    margin: 0;
    padding: 0;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
  }
  
  .main-nav {
    background: #2c3e50;
    padding: 1rem;
  }
  
  .main-nav a {
    color: white;
    text-decoration: none;
    margin-right: 2rem;
    font-weight: bold;
  }
  
  .main-nav a:hover {
    color: #3498db;
  }
  
  footer {
    background: #34495e;
    color: white;
    text-align: center;
    padding: 1rem;
    margin-top: auto;
  }
</style> 
---
// 使用嵌套布局的页面
import ArticleLayout from '../layouts/ArticleLayout.astro';
---

<ArticleLayout 
  title="嵌套布局演示" 
  author="李四" 
  date="2024年1月20日"
>
  <p>这个页面展示了嵌套布局的使用。</p>
  
  <h2>布局层次</h2>
  <ol>
    <li><strong>BaseLayout</strong> - 提供页面框架(导航、底部)</li>
    <li><strong>ArticleLayout</strong> - 提供文章结构(标题、作者信息)</li>
    <li><strong>页面内容</strong> - 这里的具体内容</li>
  </ol>
  
  <h2>嵌套的好处</h2>
  <ul>
    <li>代码复用:基础布局可以被多种内容布局使用</li>
    <li>职责分离:每个布局负责不同的功能</li>
    <li>易于维护:修改全站样式只需改基础布局</li>
  </ul>
  
  <p>这样的设计让网站结构更清晰,维护更方便!</p>
</ArticleLayout> 

效果图:

样式/CSS

src/

  • layouts/

    • Layout.astro — 基础布局

  • components/

    • ScopedStyles.astro — 作用域样式

    • GlobalStyles.astro — 全局样式

    • ClassList.astro — 动态类名

    • CSSVariables.astro — CSS 变量

    • InlineStyles.astro — 内联样式

    • ExternalStyles.astro — 外部样式表

    • SassDemo.astro — Sass 预处理器

  • styles/

    • external.css — 外部样式文件

  • pages/

    • css-demo.astro — 总览演示页面

css-demo.astro

---
// CSS 功能总览演示页面
import Layout from '../layouts/Layout.astro';
import ScopedStyles from '../components/ScopedStyles.astro';
import GlobalStyles from '../components/GlobalStyles.astro';
import ClassList from '../components/ClassList.astro';
import CSSVariables from '../components/CSSVariables.astro';
import InlineStyles from '../components/InlineStyles.astro';
import ExternalStyles from '../components/ExternalStyles.astro';
import SassDemo from '../components/SassDemo.astro';
---

<Layout title="Astro CSS 功能演示">
  <main>
    <h1>Astro CSS 功能演示</h1>
    <p class="intro">这个页面展示了 Astro 中各种 CSS 功能的使用方法</p>
    
    <!-- 全局样式演示 -->
    <section class="demo-section">
      <h2>1. 全局样式</h2>
      <GlobalStyles>
        <p class="global-title">这个标题使用全局样式</p>
        <p class="global-text">这个文本也使用全局样式</p>
      </GlobalStyles>
    </section>
    
    <!-- 作用域样式演示 -->
    <section class="demo-section">
      <h2>2. 作用域样式</h2>
      <ScopedStyles />
    </section>
    
    <!-- 动态类名演示 -->
    <section class="demo-section">
      <h2>3. 动态类名 (class:list)</h2>
      <div class="class-list-examples">
        <ClassList isActive={true} size="large" theme="dark" />
        <ClassList isActive={false} size="small" theme="light" />
      </div>
    </section>
    
    <!-- CSS 变量演示 -->
    <section class="demo-section">
      <h2>4. CSS 变量 (define:vars)</h2>
      <div class="css-vars-examples">
        <CSSVariables primaryColor="#e91e63" secondaryColor="#9c27b0" fontSize="18px" />
        <CSSVariables primaryColor="#4caf50" secondaryColor="#ff9800" fontSize="14px" />
      </div>
    </section>
    
    <!-- 内联样式演示 -->
    <section class="demo-section">
      <h2>5. 内联样式</h2>
      <InlineStyles bgColor="#fff3e0" textColor="#e65100" padding="1.5rem" />
    </section>
    
    <!-- 外部样式表演示 -->
    <section class="demo-section">
      <h2>6. 外部样式表</h2>
      <ExternalStyles />
    </section>
    
    <!-- Sass 预处理器演示 -->
    <section class="demo-section">
      <h2>7. Sass 预处理器</h2>
      <div class="sass-examples">
        <SassDemo theme="light" />
        <SassDemo theme="dark" />
      </div>
    </section>
  </main>
</Layout>

<style>
  main {
    max-width: 1200px;
    margin: 0 auto;
    padding: 2rem;
  }
  
  h1 {
    text-align: center;
    color: #2c3e50;
    margin-bottom: 1rem;
  }
  
  .intro {
    text-align: center;
    font-size: 1.2rem;
    color: #7f8c8d;
    margin-bottom: 3rem;
  }
  
  .demo-section {
    margin-bottom: 3rem;
    padding: 2rem;
    background: #f8f9fa;
    border-radius: 12px;
    border-left: 4px solid #3498db;
  }
  
  .demo-section h2 {
    color: #2c3e50;
    margin-top: 0;
    margin-bottom: 1.5rem;
  }
  
  .class-list-examples,
  .css-vars-examples,
  .sass-examples {
    display: grid;
    gap: 2rem;
  }
  
  .summary {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
    padding: 3rem;
    border-radius: 12px;
    margin-top: 3rem;
  }
  
  .summary h2 {
    text-align: center;
    margin-bottom: 2rem;
  }
  
  .features-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 1.5rem;
  }
  
  .feature-card {
    background: rgba(255,255,255,0.1);
    padding: 1.5rem;
    border-radius: 8px;
    backdrop-filter: blur(10px);
  }
  
  .feature-card h3 {
    margin-top: 0;
    margin-bottom: 0.5rem;
  }
  
  .feature-card p {
    margin-bottom: 0;
    opacity: 0.9;
  }
  
  code {
    background: rgba(255,255,255,0.2);
    padding: 0.2rem 0.4rem;
    border-radius: 3px;
    font-size: 0.9rem;
  }
  
  @media (max-width: 768px) {
    .features-grid {
      grid-template-columns: 1fr;
    }
    
    .demo-section {
      padding: 1rem;
    }
  }
</style> 

Layout.astro

---
// 基础布局组件
export interface Props {
  title: string;
  description?: string;
}

const { title, description = "Astro CSS 功能演示" } = Astro.props;
---

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="description" content={description} />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
    <title>{title}</title>
  </head>
  <body>
    <slot />
  </body>
</html>

<style is:global>
  html {
    font-family: system-ui, sans-serif;
  }
  
  body {
    margin: 0;
    padding: 0;
    background: #ffffff;
    color: #333;
    line-height: 1.6;
  }
  
  * {
    box-sizing: border-box;
  }
</style> 

全局样式

GlobalStyles.astro

---
// 全局样式演示组件
---

<div class="global-demo">
  <h2>全局样式演示</h2>
  <p>这个组件定义了全局样式</p>
  <div class="child-content">
    <slot />
  </div>
</div>

<style is:global>
  /* 全局样式 - 会影响整个页面 */
  .global-title {
    color: purple;
    text-align: center;
    font-size: 1.5rem;
  }
  
  .global-text {
    background: #ffe6f0;
    padding: 1rem;
    border-left: 4px solid purple;
  }
</style>

<style>
  /* 作用域样式 - 只影响这个组件 */
  .global-demo {
    border: 2px dashed #ccc;
    padding: 1rem;
    margin: 1rem 0;
  }
  
  h2 {
    color: #333;
    margin-top: 0;
  }
</style> 

作用域样式

ScopedStyles.astro

---
// 作用域样式演示组件
---

<div class="container">
  <h1>作用域样式标题</h1>
  <p class="text">这个文字有作用域样式</p>
  <button class="btn">按钮</button>
</div>

<style>
  /* 这些样式只会应用到这个组件内 */
  h1 {
    color: blue;
    font-size: 2rem;
  }
  
  .text {
    color: green;
    background: #f0f8ff;
    padding: 1rem;
    border-radius: 5px;
  }
  
  .btn {
    background: #ff6b6b;
    color: white;
    border: none;
    padding: 0.5rem 1rem;
    border-radius: 4px;
    cursor: pointer;
  }
  
  .btn:hover {
    background: #ff5252;
  }
</style> 

动态类名 (class:list)

ClassList.astro

---
// class:list 动态类名演示
const { isActive, size, theme } = Astro.props;

// 模拟一些条件
const isLarge = size === 'large';
const isDark = theme === 'dark';
---

<div class="demo-container">
  <h3>class:list 动态类名演示</h3>
  
  <!-- 基础用法 -->
  <div class:list={['box', { active: isActive }]}>
    基础动态类名 {isActive ? '(激活)' : '(未激活)'}
  </div>
  
  <!-- 复杂用法 -->
  <div class:list={[
    'card',
    { 
      'card--large': isLarge,
      'card--dark': isDark,
      'card--active': isActive 
    },
    size && `card--${size}`
  ]}>
    复杂动态类名
    <br />
    尺寸: {size || '默认'}
    <br />
    主题: {theme || '默认'}
  </div>
  
  <!-- 数组形式 -->
  <div class:list={['button', isActive && 'button--active', isDark && 'button--dark']}>
    数组形式的动态类名
  </div>
</div>

<style>
  .demo-container {
    padding: 1rem;
    border: 1px solid #ddd;
    border-radius: 8px;
  }
  
  .box {
    padding: 1rem;
    margin: 0.5rem 0;
    border: 2px solid #ccc;
    border-radius: 4px;
    background: #f9f9f9;
  }
  
  .box.active {
    border-color: #4CAF50;
    background: #e8f5e9;
    color: #2e7d32;
  }
  
  .card {
    padding: 1rem;
    margin: 0.5rem 0;
    border: 1px solid #ddd;
    border-radius: 6px;
    background: white;
    transition: all 0.3s ease;
  }
  
  .card--large {
    padding: 2rem;
    font-size: 1.2rem;
  }
  
  .card--dark {
    background: #333;
    color: white;
    border-color: #555;
  }
  
  .card--active {
    transform: scale(1.05);
    box-shadow: 0 4px 8px rgba(0,0,0,0.2);
  }
  
  .button {
    display: inline-block;
    padding: 0.5rem 1rem;
    margin: 0.5rem 0;
    background: #2196F3;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
  }
  
  .button--active {
    background: #4CAF50;
  }
  
  .button--dark {
    background: #424242;
  }
</style> 

CSS 变量 (define:vars)

CSSVariables.astro

---
// CSS 变量演示组件
const { primaryColor = "#3498db", secondaryColor = "#e74c3c", fontSize = "16px" } = Astro.props;

// 动态计算颜色
const lightColor = primaryColor + "20"; // 添加透明度
const borderColor = secondaryColor;
---

<div class="variables-demo">
  <h3>CSS 变量演示</h3>
  
  <div class="card primary">
    <h4>主色卡片</h4>
    <p>使用 define:vars 传递的主色</p>
  </div>
  
  <div class="card secondary">
    <h4>次色卡片</h4>
    <p>使用 define:vars 传递的次色</p>
  </div>
  
  <div class="card mixed">
    <h4>混合样式卡片</h4>
    <p>结合多个 CSS 变量的效果</p>
  </div>
</div>

<style define:vars={{ primaryColor, secondaryColor, fontSize, lightColor, borderColor }}>
  .variables-demo {
    padding: 1rem;
    font-size: var(--fontSize);
  }
  
  .card {
    padding: 1rem;
    margin: 1rem 0;
    border-radius: 8px;
    border: 2px solid var(--borderColor);
    transition: all 0.3s ease;
  }
  
  .card:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 8px rgba(0,0,0,0.1);
  }
  
  .card.primary {
    background: var(--primaryColor);
    color: white;
  }
  
  .card.secondary {
    background: var(--secondaryColor);
    color: white;
  }
  
  .card.mixed {
    background: var(--lightColor);
    color: var(--primaryColor);
    border-color: var(--primaryColor);
  }
  
  .card h4 {
    margin-top: 0;
    font-size: calc(var(--fontSize) * 1.2);
  }
</style> 

内联样式

InlineStyles.astro

---
// 内联样式演示组件
const { bgColor = "#f0f8ff", textColor = "#333", padding = "1rem" } = Astro.props;

// 动态样式对象
const dynamicStyles = {
  backgroundColor: bgColor,
  color: textColor,
  padding: padding,
  borderRadius: "8px",
  border: `2px solid ${textColor}`,
  margin: "1rem 0"
};
---

<div class="inline-demo">
  <h3>内联样式演示</h3>
  
  <!-- 字符串形式的内联样式 -->
  <div style="background: linear-gradient(45deg, #ff6b6b, #4ecdc4); color: white; padding: 1rem; border-radius: 8px; margin: 1rem 0;">
    <h4>字符串内联样式</h4>
    <p>直接写 CSS 字符串</p>
  </div>
  
  <!-- 对象形式的内联样式 -->
  <div style={{
    background: "#ffe066",
    color: "#333",
    padding: "1rem",
    borderRadius: "8px",
    border: "2px dashed #ffcc02",
    margin: "1rem 0",
    textAlign: "center"
  }}>
    <h4>对象内联样式</h4>
    <p>使用 JavaScript 对象定义样式</p>
  </div>
  
  <!-- 动态内联样式 -->
  <div style={dynamicStyles}>
    <h4>动态内联样式</h4>
    <p>通过 props 传递的动态样式</p>
    <p>背景色: {bgColor}</p>
    <p>文字色: {textColor}</p>
  </div>
  
  <!-- 混合样式 -->
  <div 
    class="mixed-style"
    style={{ 
      transform: `rotate(${Math.random() * 10 - 5}deg)`,
      boxShadow: "0 4px 8px rgba(0,0,0,0.1)"
    }}
  >
    <h4>混合样式</h4>
    <p>CSS 类 + 内联样式</p>
  </div>
</div>

<style>
  .inline-demo {
    padding: 1rem;
    border: 1px solid #ddd;
    border-radius: 8px;
  }
  
  .mixed-style {
    background: #e8f5e9;
    color: #2e7d32;
    padding: 1rem;
    border-radius: 8px;
    margin: 1rem 0;
    transition: transform 0.3s ease;
  }
  
  .mixed-style:hover {
    transform: rotate(0deg) !important;
  }
  
  h4 {
    margin-top: 0;
  }
</style> 

外部样式表

ExternalStyles.astro

---
// 外部样式表演示组件
import '../styles/external.css';
---

<div class="external-demo">
  <h3>外部样式表演示</h3>
  
  <div class="external-card">
    <h4>外部样式卡片</h4>
    <p class="utility-text">
      这个卡片使用了从 <span class="highlight">external.css</span> 导入的样式
    </p>
    <button class="external-button">外部样式按钮</button>
  </div>
  
  <div class="info-box">
    <h4>导入方式</h4>
    <pre><code>import '../styles/external.css';</code></pre>
    <p>在组件顶部导入外部 CSS 文件</p>
  </div>
</div>

<style>
  .external-demo {
    padding: 1rem;
    border: 1px solid #ddd;
    border-radius: 8px;
  }
  
  .info-box {
    background: #f8f9fa;
    padding: 1rem;
    border-radius: 6px;
    border-left: 4px solid #007acc;
  }
  
  .info-box h4 {
    margin-top: 0;
    color: #007acc;
  }
  
  pre {
    background: #e9ecef;
    padding: 0.5rem;
    border-radius: 4px;
    overflow-x: auto;
  }
  
  code {
    font-family: 'Courier New', monospace;
    color: #d63384;
  }
</style> 

external.css

/* 外部样式表 - external.css */

.external-card {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  padding: 2rem;
  border-radius: 12px;
  margin: 1rem 0;
  box-shadow: 0 8px 16px rgba(0,0,0,0.1);
}

.external-card h4 {
  margin-top: 0;
  font-size: 1.5rem;
}

.external-button {
  background: rgba(255,255,255,0.2);
  color: white;
  border: 2px solid rgba(255,255,255,0.3);
  padding: 0.5rem 1rem;
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.3s ease;
}

.external-button:hover {
  background: rgba(255,255,255,0.3);
  transform: translateY(-2px);
}

.utility-text {
  font-size: 1.1rem;
  line-height: 1.6;
}

.highlight {
  background: rgba(255,255,255,0.2);
  padding: 0.2rem 0.5rem;
  border-radius: 4px;
} 

Sass 预处理器

SassDemo.astro

---
// Sass 预处理器演示组件
const { theme = 'light' } = Astro.props;
---

<div class="sass-demo" data-theme={theme}>
  <h3>Sass 预处理器演示</h3>
  
  <div class="card">
    <h4>Sass 功能展示</h4>
    <p>这个组件使用了 Sass 的高级功能</p>
    
    <div class="features">
      <div class="feature">变量</div>
      <div class="feature">嵌套</div>
      <div class="feature">混合器</div>
      <div class="feature">函数</div>
    </div>
    
    <button class="btn btn--primary">主要按钮</button>
    <button class="btn btn--secondary">次要按钮</button>
  </div>
</div>

<style lang="scss">
  // Sass 变量
  $primary-color: #3498db;
  $secondary-color: #e74c3c;
  $border-radius: 8px;
  $spacing: 1rem;
  
  // Sass 混合器
  @mixin button-style($bg-color, $text-color: white) {
    background: $bg-color;
    color: $text-color;
    border: none;
    padding: $spacing * 0.5 $spacing;
    border-radius: $border-radius * 0.5;
    cursor: pointer;
    transition: all 0.3s ease;
    margin-right: $spacing * 0.5;
    
    &:hover {
      background: darken($bg-color, 10%);
      transform: translateY(-2px);
    }
  }
  
  // Sass 函数
  @function calculate-shadow($depth) {
    @return 0 #{$depth * 2}px #{$depth * 4}px rgba(0,0,0,0.1);
  }
  
  .sass-demo {
    padding: $spacing;
    border: 1px solid #ddd;
    border-radius: $border-radius;
    
    // 嵌套选择器
    .card {
      background: white;
      padding: $spacing * 1.5;
      border-radius: $border-radius;
      box-shadow: calculate-shadow(2);
      margin: $spacing 0;
      
      h4 {
        margin-top: 0;
        color: $primary-color;
        
        // 深层嵌套
        &::after {
          content: " ✨";
        }
      }
      
      .features {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
        gap: $spacing * 0.5;
        margin: $spacing 0;
        
        .feature {
          background: lighten($primary-color, 40%);
          color: $primary-color;
          padding: $spacing * 0.5;
          border-radius: $border-radius * 0.5;
          text-align: center;
          font-weight: bold;
          
          &:hover {
            background: lighten($primary-color, 30%);
          }
        }
      }
    }
    
    // 使用混合器
    .btn {
      &--primary {
        @include button-style($primary-color);
      }
      
      &--secondary {
        @include button-style($secondary-color);
      }
    }
    
    // 主题切换
    &[data-theme="dark"] {
      background: #2c3e50;
      color: white;
      
      .card {
        background: #34495e;
        color: white;
      }
    }
  }
</style> 

字体

看官方文档就行,很详细

语法高亮

Astro 内置了对 ShikiPrism 的支持。这为以下内容提供了语法高亮:

  • 所有在 Markdown 或 MDX 文件中使用的 代码围栏 (```)

  • .astro 文件中使用 内置的 <Code /> 组件(由 Shiki 提供支持)的内容。

  • .astro 文件中使用 <Prism /> 组件(由 Prism 提供支持)的内容。

添加 社区集成,如 Expressive Code ,以便在代码块中获取更多的文本标记和注解选项。

特性

Shiki (默认)

Prism

说明

性能

✅ 构建时处理

⚠️ 运行时处理

Shiki 在构建时生成,更快

主题支持

✅ 丰富的内置主题

✅ 可自定义样式

两者都支持主题定制

语言支持

✅ 100+ 语言

✅ 200+ 语言

都支持主流编程语言

包大小

✅ 无运行时

⚠️ 需要客户端 JS

Shiki 不增加包大小

配置复杂度

✅ 开箱即用

⚠️ 需要额外配置

Shiki 默认集成

那这意味shiki更好么?

看需求,我个人使用场景下,prism更适合我,引用为一个js组件,检测code自动赋值这种方式更适合我

🎯 核心问题

Astro Shiki 的根本局限性:

  • 构建时处理 - 只能处理构建时已知的代码

  • 📝 静态内容导向 - 适合预定义的 Markdown/代码

  • 无法处理动态内容 - CMS、用户输入、实时内容等

📊 使用场景对比

客户端方案(Prism、Highlight.js、CodeMirror、Monaco Editor

内容类型

Astro Shiki

客户端方案

典型应用

🏠 静态网站

✅ 完美

⚠️ 过度

技术文档、个人博客

🌐 CMS 网站

❌ 无法使用

✅ 必须

企业网站、内容平台

👥 社区平台

❌ 无法使用

✅ 必须

论坛、Stack Overflow

🔧 代码编辑器

❌ 无法使用

✅ 必须

CodePen、在线 IDE