Skip to content

Latest commit

 

History

History
816 lines (710 loc) · 52.4 KB

File metadata and controls

816 lines (710 loc) · 52.4 KB

Astro

astro 语法

.astro 是增强版 HTML - 可以写逻辑、导入组件、自动优化,但最终生成纯 HTML

---
// .astro 文件 - 多了这些
// 1. 代码块(运行在服务端)
const name = 'World'
const users = ['Alice', 'Bob', 'Charlie']
---

<!-- 2. 模板语法 - 可以嵌入 JavaScript 表达式 -->
<h1>Hello {name}</h1>

<!-- 3. 动态渲染列表 -->
<ul>
  {users.map(user => <li>{user}</li>)}
</ul>

<!-- 4. 条件渲染 -->
{users.length > 0 ? (
  <p>有 {users.length} 个用户</p>
) : (
  <p>暂无用户</p>
)}

<!-- 5. 导入其他组件 -->
<NavBar />

<!-- 6. 作用域样式 -->
<style>
  /* 这个样式只影响本组件 */
  h1 { color: red; }
</style>

<!-- 7. 客户端脚本 -->
<script>
  console.log('运行在浏览器')
</script>

PixPin_2026-05-05_04-13-48

┌─────────────────────────────────────────────────────────────────────────────┐
│ 阶段 0:源代码输入                                                          │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   Button.astro                                                              │
│   ┌─────────────────────────────────────────┐                               │
│   │ ---                                    │                                │
│   │ import Base from './Base.astro'       │                                 │
│   │ interface Props { variant?: string }  │                                 │
│   │ const { variant = 'primary' } = Astro.props                             │
│   │ ---                                    │                                │
│   │ <button class:active={true}>Click</button>                              │
│   │ <style>.btn { color: red; }</style>    │                                │
│   └─────────────────────────────────────────┘                               │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 阶段 1:文件解析                                                               │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   Astro 编译器读取 .astro 文件,识别四个部分:                                   │
│                                                                             │
│   ┌──────────────────┐                                                      │
│   │ 1. Frontmatter   │  --- ... ---  (组件脚本,服务端执行)                    │
│   ├──────────────────┤                                                      │
│   │ 2. HTML 模板     │  <button>...</button> (UI 模板)                       │
│   ├──────────────────┤                                                      │
│   │ 3. Style 块   │  <style>...</style> (组件样式)                            │
│   ├──────────────────┤                                                      │
│   │ 4. Script 块     │  <script>...</script> (客户端脚本)                     │
│   └──────────────────┘                                                      │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 阶段 2:TypeScript 编译(Frontmatter 处理)                                    │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   ┌─────────────────────────────────────────────────────────────────────┐   │
│   │ 步骤 2.1:识别代码类型                                                 │   │
│   │                                                                     │   │
│   │   interface Props { variant?: string }  → 类型定义                   │   │
│   │   import Base from './Base.astro'       → 实际代码                   │   │
│   │   const { variant } = Astro.props       → 实际代码                   │   │
│   └─────────────────────────────────────────────────────────────────────┘   │
│                                    │                                        │
│                                    ▼                                        │
│   ┌─────────────────────────────────────────────────────────────────────┐   │
│   │ 步骤 2.2:类型检查                                                     │   │
│   │                                                                     │   │
│   │   TypeScript 检查 interface 定义是否正确                               │   │
│   │   • variant 类型是否匹配?                                            │   │
│   │   • 导入的模块是否存在?                                               │   │
│   │                                                                     │   │
│   │   ✅ 通过 → 继续                                                     │   │
│   │   ❌ 失败 → 报错,停止构建                                             │   │
│   └─────────────────────────────────────────────────────────────────────┘   │
│                                    │                                        │
│                                    ▼                                        │
│   ┌─────────────────────────────────────────────────────────────────────┐ │
│   │ 步骤 2.3:编译转换                                                   │ │
│   │                                                                     │ │
│   │   移除所有类型定义(interface/type)                                │ │
│   │   将 TypeScript 语法编译为 JavaScript                               │ │
│   │   执行代码,收集:                                                  │ │
│   │     • Props 定义                                                    │ │
│   │       • 组件需要接收哪些 Props?                                    │ │
│   │       • 哪些 Props 有默认值?                                       │ │
│   │     • 变量值(如 variant = 'primary')                              │ │
│   │                                                                     │ │
│   │   输出:JavaScript 逻辑代码                                         │ │
│   └─────────────────────────────────────────────────────────────────────┘ │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 阶段 3:模板编译(HTML 模板处理)                                           │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   遍历 HTML 模板,识别并处理以下语法:                                       │
│                                                                             │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │ 类型 A:Astro 指令                                                  │  │
│   ├─────────────────────────────────────────────────────────────────────┤  │
│   │                                                                      │  │
│   │  A1. class:list                                     │  │
│   │      <div class:list={['base', isActive && 'active']}>              │  │
│   │      → 计算数组:['base', true && 'active'] → ['base', 'active']    │  │
│   │      → 生成:<div class="base active">                              │  │
│   │                                                                      │  │
│   │  A2. set:html / set:text                                            │  │
│   │      <div set:html={htmlString} />                                  │  │
│   │      → 验证 HTML 安全性                                             │  │
│   │      → 直接插入 HTML 字符串                                         │  │
│   │                                                                      │  │
│   │  A3. client:*(客户端指令)                                         │  │
│   │      <Interactive client:load />                                    │  │
│   │      → 标记为需要客户端激活                                         │  │
│   │      → 生成占位符 <astro-island>                                   │  │
│   │      → 记录组件路径和 Props                                         │  │
│   │                                                                      │  │
│   │  A4. transition:*(视图过渡)                                       │  │
│   │      <div transition:name="hero">                                   │  │
│   │      → 添加视图过渡标记                                             │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                                                             │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │ 类型 B:JavaScript 表达式                                           │  │
│   ├─────────────────────────────────────────────────────────────────────┤  │
│   │                                                                      │  │
│   │  B1. 变量插值 {variable}                                           │  │
│   │      <h1>{title}</h1>                                              │  │
│   │      → 替换为变量的值                                              │  │
│   │                                                                      │  │
│   │  B2. 条件渲染 {condition && <div />}                               │  │
│   │      {isLoggedIn && <span>欢迎</span>}                             │  │
│   │      → isLoggedIn = true → 保留 <span>                              │  │
│   │      → isLoggedIn = false → 完全删除                                │  │
│   │                                                                      │  │
│   │  B3. 三元表达式 {cond ? A : B}                                     │  │
│   │      {user ? <div>A</div> : <div>B</div>}                          │  │
│   │      → 根据条件只保留一个分支                                       │  │
│   │                                                                      │  │
│   │  B4. 数组循环 {arr.map(item => <li>{item}</li>)}                   │  │
│   │      {['A','B'].map(i => <li>{i}</li>)}                            │  │
│   │      → 展开为 <li>A</li><li>B</li>                                 │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                                                             │
│   输出:处理后的 HTML 字符串                                               │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 阶段 4:组件系统处理                                                        │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │ 步骤 4.1:识别组件标签                                              │  │
│   │                                                                      │  │
│   │   <BaseButton variant="primary">Click</BaseButton>                 │  │
│   │                                                                      │  │
│   │   判断类型:                                                        │  │
│   │   • HTML 内置标签 (<div>, <span>) → 直接输出                        │  │
│   │   • Astro 组件 (<BaseButton>) → 递归编译                            │  │
│   │   • 框架组件 (<ReactButton />) → 特殊处理                           │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                    │                                        │
│                                    ▼                                        │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │ 步骤 4.2:处理 Astro 组件                                          │  │
│   │                                                                      │  │
│   │   子组件 = 递归执行阶段 1-3                                          │  │
│   │                                                                      │  │
│   │   ┌─────────────────────────────────────────────────────────────┐  │  │
│   │   │ 1. 读取 BaseButton.astro                                    │  │  │
│   │   │ 2. 编译其 Frontmatter                                       │  │  │
│   │   │ 3. 编译其模板                                               │  │  │
│   │   │ 4. 接收 Props { variant: 'primary' }                        │  │  │
│   │   │ 5. 处理 children 'Click'                                    │  │  │
│   │   │ 6. 返回生成的 HTML                                          │  │  │
│   │   └─────────────────────────────────────────────────────────────┘  │  │
│   │                                                                      │  │
│   │   合并子组件 HTML 到父组件                                          │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                    │                                        │
│                                    ▼                                        │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │ 步骤 4.3:处理框架组件(React/Vue/Svelte)                         │  │
│   │                                                                      │  │
│   │   <ReactButton onClick={handleClick}>Click</ReactButton>           │  │
│   │                                                                      │  │
│   │   • 提取 Props 和 children                                          │  │
│   │   • 生成包装器 HTML:<astro-island>                                │  │
│   │   • 序列化 Props 为 JSON                                           │  │
│   │   • 记录需要客户端水合                                             │  │
│   │   • 等待后续生成 JS 入口文件                                       │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 阶段 5:样式处理                                                            │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   收集所有 <style> 标签,分类处理:                                         │
│                                                                             │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │ 类型 1:普通样式(默认作用域)                                       │  │
│   │                                                                      │  │
│   │   <style>                                                           │  │
│   │     .btn { color: red; }                                           │  │
│   │   </style>                                                          │  │
│   │                                                                      │  │
│   │   → 生成唯一 ID:data-astro-abc123                                  │  │
│   │   → 重写选择器:.btn[data-astro-abc123] { color: red; }            │  │
│   │   → 给对应 HTML 添加属性                                            │  │
│   │                                                                      │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                                                             │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │ 类型 2:内联样式(<style is:inline>)                               │  │
│   │                                                                      │  │
│   │   → 保持原样,不添加作用域                                          │  │
│   │   → 原样输出到 HTML 中                                             │  │
│   │                                                                      │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                                                             │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │ 类型 3:全局样式(src/styles/)                                     │  │
│   │                                                                      │  │
│   │   → 不添加作用域                                                    │  │
│   │   → 提取到独立 CSS 文件                                            │  │
│   │                                                                      │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                                                             │
│   生产模式额外处理:                                                        │
│   • 合并相同组件的样式                                                    │
│   • 压缩 CSS(移除空格、注释)                                           │
│   • 添加浏览器前缀                                                       │
│   • 生成独立 .css 文件,添加哈希(如 Button.a1b2c3.css)                 │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 阶段 6:脚本处理                                                            │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   收集所有 <script> 标签,分类处理:                                        │
│                                                                             │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │ 类型 1:普通脚本                                                     │  │
│   │                                                                      │  │
│   │   <script>                                                          │  │
│   │     console.log('hello')                                           │  │
│   │   </script>                                                         │  │
│   │                                                                      │  │
│   │   → 提取内容到独立文件                                              │  │
│   │   → 添加 type="module"                                             │  │
│   │   → 处理 import 语句                                               │  │
│   │   → 生产模式:压缩、Tree Shaking                                   │  │
│   │   → 输出:/_astro/button.xyz789.js                                 │  │
│   │                                                                      │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                                                             │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │ 类型 2:内联脚本(<script is:inline>)                              │  │
│   │                                                                      │  │
│   │   → 保持原样,不提取                                                │  │
│   │   → 原样输出到 HTML 中                                             │  │
│   │                                                                      │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                                                             │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │ 类型 3:框架组件脚本(React/Vue)                                   │  │
│   │                                                                      │  │
│   │   → 生成组件入口文件                                                │  │
│   │   → 包含水合逻辑                                                    │  │
│   │   → 输出:/_astro/Component.hash.js                                │  │
│   │                                                                      │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 阶段 7:最终输出                                                            │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   组装所有编译产物:                                                        │
│                                                                             │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │ 最终 HTML 文件                                                      │  │
│   │                                                                      │  │
│   │   <!DOCTYPE html>                                                  │  │
│   │   <html>                                                           │  │
│   │   <head>                                                           │  │
│   │     <!-- 样式链接 -->                                              │  │
│   │     <link rel="stylesheet" href="/_astro/button.a1b2c3.css">      │  │
│   │   </head>                                                          │  │
│   │   <body>                                                           │  │
│   │     <!-- 组件 HTML -->                                             │  │
│   │     <button class="btn btn-primary" data-astro-abc123>            │  │
│   │       点击                                                        │  │
│   │     </button>                                                      │  │
│   │                                                                      │  │
│   │     <!-- 框架组件占位符 -->                                        │  │
│   │     <astro-island data-props="..."></astro-island>                │  │
│   │                                                                      │  │
│   │     <!-- 脚本链接 -->                                              │  │
│   │     <script type="module" src="/_astro/button.xyz789.js"></script>│  │
│   │   </body>                                                          │  │
│   │   </html>                                                          │  │
│   │                                                                      │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                                                             │
│   dist/ 目录结构:                                                          │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │   dist/                                                            │  │
│   │   ├── index.html                 # 页面 HTML                       │  │
│   │   ├── about.html                                                    │  │
│   │   ├── _astro/                    # 优化后的资源                    │  │
│   │   │   ├── button.a1b2c3.css     # 样式文件                         │  │
│   │   │   ├── button.xyz789.js      # 脚本文件                         │  │
│   │   │   ├── logo.abc123.webp      # 优化后的图片                     │  │
│   │   │   └── chunk.xxx.js          # 公共代码块                       │  │
│   │   └── public/                    # 静态资源                        │  │
│   │       └── favicon.svg                                              │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 阶段 8:浏览器运行时                                                        │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │ 步骤 8.1:加载页面                                                  │  │
│   │                                                                      │  │
│   │   浏览器请求 → 服务器返回 HTML → 解析 DOM                          │  │
│   │                                                                      │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                    │                                        │
│                                    ▼                                        │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │ 步骤 8.2:加载资源                                                  │  │
│   │                                                                      │  │
│   │   • 并行加载 CSS 文件                                              │  │
│   │   • 并行加载 JS 文件                                               │  │
│   │   • 加载图片等静态资源                                             │  │
│   │                                                                      │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                    │                                        │
│                                    ▼                                        │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │ 步骤 8.3:执行脚本                                                  │  │
│   │                                                                      │  │
│   │   • 执行普通 JS 脚本                                               │  │
│   │   • 水合框架组件:                                                 │  │
│   │     - 扫描 <astro-island> 占位符                                   │  │
│   │     - 加载对应的组件 JS                                            │  │
│   │     - 激活组件(绑定事件、状态等)                                 │  │
│   │                                                                      │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                    │                                        │
│                                    ▼                                        │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │ 最终结果                                                            │  │
│   │                                                                      │  │
│   │   用户看到完整页面,可交互                                           │  │
│   │   SEO 搜索引擎看到完整 HTML 内容                                    │  │
│   │                                                                      │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
运行环境 位置 能做什么 不能做什么
服务端(Astro 组件脚本) 构建时的 Node.js 读文件、访问数据库、调用 API、导入模块 访问 window、document、localStorage
客户端(浏览器) 用户的浏览器 DOM 操作、事件监听、用户交互 读服务器文件、访问环境变量

Astro 对象从哪来:编译器注入

Astro 不是全局对象,而是 Astro 编译器编译 .astro 文件时,自动在 frontmatter 脚本外层包一层并注入的局部变量(包含 props、url、request 等)。所以组件内无需 import 就能直接使用 Astro.props,但它只在 .astro 文件内有意义:

// Astro 编译后自动生成的包裹代码(你看不到)
async function render() {
  const Astro = { props, url, request, ... }  // ← 自动注入
  // ↓ 你的代码从这里开始
  const { post } = Astro.props
  // ↑ 你的代码到这里结束
}

它看起来像"全局对象",但实际是每个 .astro 组件独有的作用域变量。对比各文件的"魔法变量"来源:

变量 在哪个文件可用 来源
Astro 所有 .astro 组件 编译器注入
frontmatter .mdx / .md 页面 编译器注入
Astro.props 接收 props 的组件 由父级传入

注意:不能在 .astro 之外用 Astro。普通 .ts / .js 工具函数里不存在 Astro.props,数据应由组件内取到后再传给纯函数,单向流入。

核心增强

一、组件化

组件化 = 可复用的 UI 片段 + 封装的状态和样式

1、Props 传递机制

interface 是 TypeScript 的类型系统,不是 JavaScript 语法。只在编译时运行(类型检查),运行时完全消失

Props 让 Astro 组件像函数一样可组合,父组件通过属性传参,子组件通过 Astro.props 接收,构建时生成最终的 HTML

---
// 1. 定义组件 Props 类型
interface Props {
  title: string
  onClick?: () => void
}

// 2. 使用类型
const { title, onClick } = Astro.props

// 3. 类型守卫(编译时)
if (typeof onClick === 'function') {
  // 运行时检查,不是类型检查
}
---

<!-- 4. 模板中使用 -->
<h1>{title}</h1>

案例1:博客文章卡片

---
// PostCard.astro
interface Props {
  title: string
  excerpt: string
  date: Date
  tags: string[]
  coverImage?: string
  readingTime: number
}

const { 
  title, 
  excerpt, 
  date, 
  tags, 
  coverImage, 
  readingTime 
} = Astro.props
---

<article class="post-card">
  {coverImage && (
    <img src={coverImage} alt={title} class="cover" />
  )}
  
  <div class="content">
    <h2>{title}</h2>
    <p class="excerpt">{excerpt}</p>
    
    <div class="meta">
      <time datetime={date.toISOString()}>
        {date.toLocaleDateString('zh-CN')}
      </time>
      <span class="reading-time">{readingTime} 分钟阅读</span>
    </div>
    
    <div class="tags">
      {tags.map(tag => (
        <span class="tag">{tag}</span>
      ))}
    </div>
  </div>
</article>
---
// 在列表页使用
import PostCard from '../components/PostCard.astro'

const posts = await getCollection('blogs')
---

<div class="posts-grid">
  {posts.map(post => (
    <PostCard
      title={post.data.title}
      excerpt={post.data.description}
      date={post.data.pubDate}
      tags={post.data.tags}
      coverImage={post.data.cover?.src}
      readingTime={post.data.minutesRead}
    />
  ))}
</div>

案例2:多层传递

---
// Level1.astro - 顶层组件
import Level2 from './Level2.astro'

const userData = {
  name: '李四',
  level: 'gold',
  points: 1500
}
---

<Level2 user={userData} />
---
// Level2.astro - 中间组件
import Level3 from './Level3.astro'

const { user } = Astro.props
---

<div>
  <h2>用户:{user.name}</h2>
  <Level3 user={user} />
</div>
---
// Level3.astro - 底层组件
const { user } = Astro.props
---

<div class="user-detail">
  <p>等级:{user.level}</p>
  <p>积分:{user.points}</p>
</div>
2、样式封装
<style>
  /* 这个样式只会影响当前组件 */
  .btn {
    padding: 0.5rem 1rem;
    border-radius: 0.25rem;
  }
  
  .btn-primary {
    background: blue;
    color: white;
  }
</style>

编译后:

<!-- Astro 自动添加唯一属性 -->
<style>
  .btn[data-astro-abc123] { ... }
  .btn-primary[data-astro-abc123] { ... }
</style>

<button class="btn btn-primary" data-astro-abc123>
  点击我
</button>
3、组件嵌套
---
// Card.astro - 使用 Button 组件
import Button from './Button.astro'

const { title } = Astro.props
---

<div class="card">
  <h3>{title}</h3>
  <p><slot /></p>
  <Button variant="primary">确认</Button>
  <Button variant="secondary">取消</Button>
</div>

二、插槽系统

插槽 = 占位符,让父组件可以"注入"内容到子组件的指定位置

PixPin_2026-05-04_00-43-21

1、默认插槽(任意内容)
---
// Layout.astro
---

<div class="layout">
  <header>
    <slot name="header" />  <!-- 只接收 slot="header" 的内容 -->
  </header>
  
  <main>
    <slot />  <!-- 接收没有 name 的内容 -->
  </main>
  
  <footer>
    <slot name="footer" />  <!-- 只接收 slot="footer" 的内容 -->
  </footer>
</div>
// 使用 Layout
<Layout>
  <div slot="header">导航栏</div>
  
  <article>主要内容</article>  <!-- 自动进入默认 slot -->
  
  <div slot="footer">版权信息</div>
</Layout>
2、具名插槽(命名区域)
---
// layouts/Documentation.astro
---

<div class="doc-layout">
  <!-- 定义多个命名区域 -->
  <aside>
    <slot name="sidebar" />  <!-- 侧边栏区域 -->
  </aside>
  
  <main>
    <slot name="before-content" />  <!-- 内容前区域 -->
    <slot />                         <!-- 主要内容区域 -->
    <slot name="after-content" />   <!-- 内容后区域 -->
  </main>
  
  <div class="extra">
    <slot name="footer-extra" />    <!-- 额外区域 -->
  </div>
</div>
// 使用 - 可以填充各种区域
<Documentation>
  <!-- ✅ 填充定义的侧边栏 -->
  <div slot="sidebar">
    <ul>目录</ul>
  </div>
  
  <!-- ✅ 填充内容前区域 -->
  <div slot="before-content">
    <div class="notice">提示信息</div>
  </div>
  
  <!-- ✅ 默认插槽 - 主要内容 -->
  <h1>文档标题</h1>
  <p>文档内容...</p>
  
  <!-- ✅ 填充内容后区域 -->
  <div slot="after-content">
    <div class="share">分享组件</div>
  </div>
  
  <!-- ✅ 填充额外区域 -->
  <div slot="footer-extra">
    <div class="related">相关文章</div>
  </div>
</Documentation>

三、模版指令

模板指令 = 编译时的 DOM 操作标记,构建时转化为静态 HTML

PixPin_2026-05-04_00-52-57

Astro 指令最终都会被编译成纯 HTML(以及可选的客户端 JavaScript),这就是 Astro"零 JS 默认"的核心原理。这些指令是 Astro 团队预先实现的,就像 Vue 的 v-if、React 的 className 一样

唯一会产生 JS 的指令:client:load 等客户端指令

指令 作用 示例
class:list 动态类名列表 class:list={['base', active && 'active']}
class:value 条件类名 class:active={isActive}
set:html 设置 innerHTML set:html={htmlString}
set:text 设置 textContent set:text={textString}
is:inline 禁用组件包装 <style is:inline>
client:load 客户端加载组件 <Component client:load />
client:visible 可见时加载 <Component client:visible />
client:idle 空闲时加载 <Component client:idle />
client:media 媒体查询匹配时加载 <Component client:media="(max-width: 768px)" />
transition:name 视图过渡动画 <div transition:name="hero">
transition:animate 自定义过渡动画 <div transition:animate="slide">

指令的编译过程:

// 1. 源代码
const condition = true
const result = <div class:active={condition}>内容</div>

// 2. Astro 编译时执行
// condition = true 被计算

// 3. 生成 HTML
`<div class="active">内容</div>`

// 4. 最终输出(无任何运行时痕迹)

四、布局支持

布局 = 页面包装器,提供一致的页面结构

PixPin_2026-05-04_00-48-12

对照 YuBlog 的开发流程

上面是 Astro 通用语法。这个仓库里对应关系如下。

  1. 内容结构

    首页 /、标签 /tags/、归档 /archives/、项目 /projects/、关于 /about/、友链 /friends/、文章 /blogs/[slug]/。

  2. 布局

    • src/layouts/BaseLayout.astro:Head、顶栏、背景、页脚
    • src/layouts/BlogIndexLayout.astro:导航页面的居中单栏容器
    • src/layouts/StandardLayout.astro:文章详情
  3. Markdown 文章

    放在 src/content/blogs/,不是 posts/。

    ---
    title: 我的第一篇博客
    pubDate: 2026-01-01
    category: 技术向
    tags: [Astro]
    ---
    这里是内容...
  4. 动态路由

    src/pages/blogs/[...slug].astro 读 blogs 集合并渲染。

  5. 构建

    pnpm build    # 输出到 dist/,随后 Pagefind 建搜索索引

    dist/ 是构建产物,不进 git。

站点级行为与模块边界见 项目解析。