Chapter 02

项目结构与文件路由

掌握 Astro 约定式文件路由规则、各目录职责分工与配置文件详解

Astro 的约定式文件路由

src/pages/ 目录是路由的核心

在 Astro 中,src/pages/ 目录下的每个文件都自动映射为一个 URL 路径。这种「文件即路由」的约定(Convention over Configuration)意味着你不需要写任何路由配置代码。

src/pages/
├── index.astro          → /
├── about.astro          → /about
├── blog/
│   ├── index.astro      → /blog
│   └── first-post.astro → /blog/first-post
├── contact.astro        → /contact
└── 404.astro            → 自定义 404 页面

支持的文件格式

.astro
Astro 原生组件格式,包含 frontmatter(JavaScript/TypeScript)+ HTML 模板 + 作用域 CSS,是最常用的页面格式。
.md / .mdx
Markdown 和 MDX 文件也可以直接放在 pages/ 中作为页面。适合文档站和博客文章。
.ts / .js
纯 JS/TS 文件在 pages/ 中用于创建 API 端点(返回 JSON/Response,而不是 HTML)。

完整目录结构说明

my-astro-project/
├── src/
│   ├── pages/           # 页面 + API 路由(必须)
│   ├── layouts/         # 页面布局模板(约定)
│   │   └── BaseLayout.astro
│   ├── components/      # 可复用组件(约定)
│   │   ├── Header.astro
│   │   └── Button.jsx   # 也可以是 React/Vue 组件
│   ├── content/         # 内容集合(第4章详解)
│   │   └── blog/
│   │       └── config.ts
│   ├── styles/          # 全局样式(约定)
│   │   └── global.css
│   ├── utils/           # 工具函数
│   └── env.d.ts         # TypeScript 类型声明
├── public/              # 静态资源(原样复制,不处理)
│   ├── favicon.svg
│   ├── robots.txt
│   └── images/
├── astro.config.mjs     # Astro 配置(必须)
├── tsconfig.json        # TypeScript 配置
└── package.json

src/env.d.ts 类型声明

Astro 会自动生成这个文件,为 Astro 专有的全局类型提供声明。如果缺少,你可能会看到 astro:contentastro:assets 等模块的类型找不到的错误。

/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />

// 可以在这里扩展 Astro 的类型
declare namespace App {
  interface Locals {
    // SSR 模式下每次请求可用的数据
    user?: { id: string; name: string; };
  }
}

public/ 与 src/ 中静态资源的区别

public/ 目录

放置完全静态的资源:favicon、robots.txt、sitemap.xml 等。Astro 会原样复制到 dist/,不做任何处理(不压缩、不哈希文件名)。通过根路径 /filename.svg 访问。

src/ 中的资源

放在 src/ 的图片、CSS 等资源会经过 Astro 的构建流水线处理:压缩、加哈希指纹(缓存破坏)、生成 WebP/AVIF。推荐将需要优化的图片放在 src/assets/

路由优先级与特殊路由

路由匹配优先级

当多个路由规则可能匹配同一 URL 时,Astro 按以下优先级选择:

  1. 静态路由(如 src/pages/blog.astro)优先于动态路由
  2. 命名动态段(如 [slug].astro)优先于剩余参数([...slug].astro
  3. 更深层级的路径优先于父级通配符
src/pages/
├── blog.astro           # 优先匹配 /blog(静态)
├── [slug].astro         # 匹配 /任意单段
└── [...path].astro      # 最后才匹配 /任意多段

404 页面

---
// src/pages/404.astro
import BaseLayout from '../layouts/BaseLayout.astro';
---

<BaseLayout title="页面未找到">
  <h1>404 — 页面不存在</h1>
  <p>您访问的页面已移除或地址有误。</p>
  <a href="/">返回首页</a>
</BaseLayout>
astro.config.mjs 的 trailingSlash 设置

默认情况下,/about/about/ 都能访问。通过 trailingSlash: 'always'(强制末尾斜杠)或 'never'(强制无末尾斜杠)可以统一 URL 格式,对 SEO 有帮助。

astro.config.mjs 配置文件详解

// astro.config.mjs
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import tailwind from '@astrojs/tailwind';
import mdx from '@astrojs/mdx';

export default defineConfig({
  // 站点基础 URL(生成 sitemap 和 canonical 链接时使用)
  site: 'https://mysite.com',

  // 子路径部署(如部署到 GitHub Pages 的 /my-repo/ 路径下)
  base: '/',

  // 末尾斜杠:'ignore'(默认)| 'always' | 'never'
  trailingSlash: 'ignore',

  // 渲染模式:'static'(默认)| 'server'(SSR)| 'hybrid'
  output: 'static',

  // 集成插件(UI 框架、工具)
  integrations: [
    react(),           // React 组件支持
    tailwind(),        // Tailwind CSS
    mdx(),             // MDX 支持
  ],

  // Vite 底层配置(Astro 基于 Vite 构建)
  vite: {
    define: {
      'import.meta.env.BUILD_DATE': JSON.stringify(new Date().toISOString()),
    },
  },
});

动态路由与参数

---
// src/pages/blog/[slug].astro
// 方括号 [slug] 表示动态参数,匹配 /blog/任意文字

// 静态模式(SSG)必须导出 getStaticPaths
export async function getStaticPaths() {
  const posts = await fetch('/api/posts').then(r => r.json());
  return posts.map(post => ({
    params: { slug: post.slug },   // slug 参数值
    props: { post },               // 传递给页面的 props
  }));
}

// 从 props 获取当前页面数据
const { post } = Astro.props;
const { slug } = Astro.params;
---

<h1>{post.title}</h1>
<p>slug: {slug}</p>
---
// src/pages/docs/[...path].astro
// [...path] 剩余参数,匹配任意深度的路径
// /docs/getting-started → path = "getting-started"
// /docs/api/reference   → path = "api/reference"

const { path } = Astro.params;
const segments = path?.split('/') ?? [];
---

本章小结

本章核心要点