
Astro的基础了解与对比
安装在之前文档已经写过了,现在从头了解一下Astro基础结构,其实Astro大部分功能对我来说并不需要用,我的主要目的是通过Astro生成一套静态HTML JS CSS,可适配任意CMS的纯静态,而不是现代化的使用方式、
Astro系:追求极致性能和现代化开发体验,通过组件化和最少化客户端JS来构建高效、易维护的(静态)网站,非常适合做高质量的CMS主题模板。
传统系:上手快,生态成熟,适合快速搭建功能页面或维护旧项目,但在性能和大型项目可维护性方面可能稍逊一筹。
技术栈对比:Astro系 vs. 传统系
前提条件
Node.js -
v18.17.1
或v20.3.0
、v22.0.0
或更高版本。(v19
和v21
均不支持。)文本编辑器 - 我们推荐使用 VS Code 并安装 官方 Astro 扩展。
终端 - Astro 通过其命令行界面(CLI)访问。
项目结构
一、核心目录与文件
每个 Astro 项目都包含以下关键部分:
src/
: 存放你的项目源代码,包括页面、组件、布局和样式。Astro 会处理并构建此目录中的文件。public/
: 存放静态资源,如字体、图标或不需要构建处理的文件。此目录内容会直接复制到最终输出。package.json
: 项目的依赖清单和脚本命令(如npm run dev
,npm run build
)。astro.config.mjs
: Astro 项目的配置文件,用于设置集成、构建选项等。tsconfig.json
: TypeScript 配置文件,用于改善编辑器支持和类型检查。
二、src/
目录详解
src/
目录是你项目代码的主要存放地。以下是其标准子目录:
src/pages/
: (必需) 此目录下的文件会自动成为网站的页面。例如:
src/pages/index.astro
对应网站首页 (/
)。例如:
src/pages/about.astro
对应/about
页面。支持
.astro
,.md
,.mdx
(需集成) 文件。
src/components/
: 存放可复用的 UI 组件。例如:
Header.astro
,Button.astro
。可以是
.astro
文件或其他框架组件 (如 React, Vue,需集成)。
src/layouts/
: 存放定义页面结构的布局组件。例如:
MainLayout.astro
用于包裹多个页面,提供统一的页眉页脚。
src/styles/
: (约定俗成) 存放全局或局部样式文件。例如:
global.css
。
src/assets/
: (约定俗成) 存放需要 Astro 处理和优化的资源,如图片。与public/
不同,此处的资源会经过构建流程。src/content/
: (可选,用于内容集合) 存放 Markdown、JSON/YAML 等内容数据,通过 Astro 的内容集合功能进行类型安全的管理。通常会有一个src/content/config.ts
(或.js
) 配置文件。
三、public/
目录
用于存放不需要 Astro 构建过程处理的静态文件。这些文件会原样复制到最终的构建输出目录。
常见用途:
robots.txt
,favicon.svg
, 字体文件, 不需要处理的图片。
四、配置文件
astro.config.mjs
: 配置 Astro 项目的行为,如添加集成、服务器选项等。tsconfig.json
: 配置 TypeScript,即使项目主要使用 JavaScript,它也能增强开发体验。
五、标准项目结构示例
├── public/
│ ├── favicon.svg
│ └── robots.txt
├── src/
│ ├── assets/
│ │ └── logo.png
│ ├── components/
│ │ └── Header.astro
│ ├── content/ # (可选) 用于内容集合
│ │ ├── blog/
│ │ │ └── my-first-post.md
│ │ └── config.ts
│ ├── layouts/
│ │ └── MainLayout.astro
│ ├── pages/
│ │ ├── index.astro
│ │ └── about.astro
│ └── styles/
│ └── global.css
├── astro.config.mjs
├── package.json
└── tsconfig.json
开发与构建
核心命令
npm run dev
: 启动开发模式,实时预览。npm run build
: 构建生产版本。npm run preview
: 本地预览构建后的站点。
页面
这一功能我也许只会用到.astro用来构建静态html,但我也要了解一下它。
1. Astro 页面基础
定义:位于项目
src/pages/
目录内的文件。核心机制:该目录下的每个文件都会依据其文件路径,自动成为网站上的一个可访问页面或 API 端点。
2. 支持的页面文件类型
Astro 在 src/pages/
目录中支持以下文件类型:
.astro
:Astro 组件,用于构建包含动态逻辑和 UI 的完整页面。.md
:Markdown 文件,非常适合内容驱动的页面,如博客文章、文档。.mdx
:MDX 文件,结合 Markdown 和 JSX 的能力(需安装 MDX 集成)。.html
:标准 HTML 文件,可直接作为静态页面。.js
/.ts
:用作 API 端点 (Endpoints),处理服务器端逻辑并返回数据(例如 JSON)。
3. 路由策略:基于文件的路由
Astro 采用基于文件的路由 (File-based Routing):
路径映射:
src/pages/
目录中文件的路径直接决定了其在网站上的 URL。src/pages/about.astro
->your-site.com/about
src/pages/blog/my-post.md
->your-site.com/blog/my-post
动态路由:支持通过动态参数生成多个页面,常用于从内容集合或 CMS 加载数据创建页面。
4. 页面间链接
使用标准的 HTML <a>
标签进行页面跳转。关键是使用相对于根域名的 URL 路径。
示例:链接到 your-site.com/blog/my-post
:
<a href="/blog/my-post">阅读文章</a>
5. .astro
页面(主要使用)
.astro
文件是构建页面的核心:
功能:支持所有 Astro 组件特性(JS Frontmatter 脚本、JSX 风格模板等)。
输出:必须生成完整的 HTML 文档。Astro 默认会自动添加
<!DOCTYPE html>
和基础<head>
。布局组件:为避免重复 HTML 结构,推荐使用布局组件 (Layout Components) 将共享的
<head>
,<body>
等元素抽离。
示例 (.astro
页面使用布局):
---
// src/pages/about.astro
import Layout from '../layouts/Layout.astro';
---
<Layout title="About">
<a href="/blog/my-post">阅读文章</a>
</Layout>
6. Markdown (.md
) 和 MDX (.mdx
) 页面
非常适合创建以内容为中心的页面:
自动处理:Astro 会自动将这些文件渲染为页面。
布局指定:通过文件的前置元数据 (frontmatter) 中的
layout
属性,可以指定一个布局组件来包裹 Markdown/MDX 内容,形成完整 HTML 页面。内容集合:对于结构相似的 Markdown 内容(如系列博客),推荐使用内容集合 (Content Collections) 进行管理。
示例 (Markdown 页面使用布局):
---
# src/pages/posts/my-post.md
layout: ../../layouts/Layout.astro
title: 我的第一篇文章
publishDate: 2025-05-24
---
# 文章标题
这里是 Markdown 正文内容...
这个我也用不到
7. HTML 页面 (.html
)
纯静态页面,用不到
可以直接将 .html
文件放置在 src/pages/
中作为静态页面。但请注意,纯 HTML 文件不支持 Astro 的一些高级功能(如组件化、脚本优化等)。
8. 自定义错误页面
404 页面 (未找到):
创建
src/pages/404.astro
或src/pages/404.md
。Astro 会用它生成
404.html
,多数部署服务会自动使用。
500 页面 (服务器内部错误):
创建
src/pages/500.astro
(仅适用于按需渲染的页面,不能预渲染)。error
属性 (Astro v4.11.0+): 此页面会自动接收一个error
属性,包含渲染时抛出的错误对象。可用于向用户展示错误信息(注意:避免直接暴露敏感信息,如堆栈跟踪)。
示例 (
500.astro
处理错误):--- // src/pages/500.astro interface Props { error: unknown; } const { error } = Astro.props; // 生产环境中应记录完整错误,但只向用户展示通用信息 let displayMessage = "服务器处理请求时发生错误。"; if (error instanceof Error) { console.error("500 Error:", error.message, error.stack); // 记录详细错误 // 可以根据 error.name 或特定错误类型定制 displayMessage } --- <h1>抱歉,出错了</h1> <p>{displayMessage}</p>
9. 局部页面 (Partials) - 高级功能(我用不到)
(Astro v3.4.0+) 主要用于与 htmx、Unpoly 等库配合,实现页面局部动态更新,无需整页刷新。
1. 布局文件 (例如 src/layouts/BaseLayout.astro
)
代码段
---
// src/layouts/BaseLayout.astro
import '../styles/global.css'; // 确保这里引入了 Tailwind 和 daisyUI
export interface Props {
title?: string;
}
const { title = "HTMX + daisyUI" } = Astro.props;
---
<!doctype html>
<html lang="zh-CN" data-theme="cupcake"> {/* 使用一个 daisyUI 主题 */}
<head>
<meta charset="UTF-8" />
<title>{title}</title>
<meta name="viewport" content="width=device-width" />
<script src="https://unpkg.com/htmx.org@1.9.10"></script> {/* HTMX 库 */}
</head>
<body class="min-h-screen bg-base-200 flex flex-col items-center pt-10">
<main class="w-full max-w-lg p-4">
<slot />
</main>
</body>
</html>
2. 主页面 (例如 src/pages/simple-htmx.astro
)
代码段
---
// src/pages/simple-htmx.astro
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title="简洁 HTMX 示例">
<div class="text-center p-6 bg-base-100 rounded-box shadow-lg">
<h1 class="text-2xl font-bold mb-4">动态内容演示</h1>
{/* HTMX 将内容加载到这里 */}
<div id="dynamic-content-placeholder" class="p-4 mb-4 border border-dashed border-neutral-content/50 rounded-md min-h-[50px] bg-base-200">
初始占位内容
</div>
<button
class="btn btn-accent"
hx-get="/partials/simple-update"
hx-target="#dynamic-content-placeholder"
hx-swap="innerHTML"
>
获取新内容
</button>
</div>
</BaseLayout>
3. 局部页面 (Partial Page - src/pages/partials/simple-update.astro
)
代码段
---
// src/pages/partials/simple-update.astro
export const partial = true; // 标记为局部页面
const messages = ["你好,daisyUI!👋", "Astro Partials 很方便!✨", "HTMX 让局部更新变简单!🚀"];
const randomMessage = messages[Math.floor(Math.random() * messages.length)];
---
{/* 这部分 HTML 将被发送回客户端 */}
<div class="alert alert-success shadow-md">
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
<span>{randomMessage} (来自服务器 @ {new Date().toLocaleTimeString()})</span>
</div>
这个简洁案例做了什么:
主页面 (
simple-htmx.astro
):使用了一个简单的 daisyUI 布局 (
bg-base-100
,rounded-box
,shadow-lg
)。有一个目标
div
(#dynamic-content-placeholder
),用虚线边框和背景色标出。一个 daisyUI 按钮 (
btn btn-accent
),通过 HTMX 的hx-get
属性从/partials/simple-update
获取内容,并用hx-target
和hx-swap
更新目标div
。
局部页面 (
simple-update.astro
):标记为
partial = true
。返回一个简单的 daisyUI
alert alert-success
组件,其中包含一条随机消息和当前时间,以表明内容是动态生成的。
感觉类似于ajax pjax 但语法等相关更为简单,不过我用不到,并不适用我现在的需求
路由
1. 核心机制:基于文件的路由
定义:您项目
src/pages/
目录下的文件结构 直接决定了网站的 URL 链接。便捷性:无需额外路由配置文件,增删
src/pages/
目录中的文件即可自动管理路由。
2. 页面导航:标准 <a>
标签
使用标准的 HTML
<a>
元素进行页面跳转。关键:
href
属性使用相对于根域名的 URL 路径。
示例:
<a href="/about/">关于我们</a>
<a href="/blog/my-first-post/">阅读文章</a>
3. 静态路由:直接映射
src/pages/
目录中的 .astro
, .md
, .mdx
文件会自动成为静态页面,其 URL 与文件路径一致。在执行 astro build
时,这些文件会被处理并生成对应的静态 HTML 文件放入 dist/
目录。
示例:
src/pages/index.astro
-> 构建后生成dist/index.html
(对应your-site.com/
)src/pages/contact.astro
-> 构建后生成dist/contact.html
(对应your-site.com/contact
)src/pages/products/shoes.md
-> 构建后生成dist/products/shoes.html
(对应your-site.com/products/shoes
)
提示:可通过
astro.config.mjs
中的build.format
自定义输出文件的路径格式 (如page.html
vspage/index.html
),这会影响astro build
后dist/
目录中的文件结构。
4. 动态路由 (SSG 模式):getStaticPaths()
在静态站点生成 (SSG) 模式下,为动态生成多个页面(如博客文章详情页),文件名需包含方括号 []
指定动态参数。
构建时执行
getStaticPaths()
函数:此异步函数在astro build
期间执行,告诉 Astro 要为哪些参数值生成对应的静态 HTML 页面。返回值决定构建内容:一个对象数组,每个对象包含一个
params
属性,其键对应文件名中的动态参数。每个对象都会在构建时生成一个独立的 HTML 页面。访问参数 (构建时):页面内通过
Astro.params
获取参数值,用于在构建时生成特定内容的 HTML。
示例 1:单个参数 [id].astro
---
// src/pages/posts/[id].astro
export async function getStaticPaths() {
// 假设 allPosts 是在构建时从API或本地文件获取的文章数据
const allPosts = [{ id: "1", title: "..." }, { id: "2", title: "..." }];
return allPosts.map(post => ({
params: { id: post.id },
props: { postData: post } // 可选:传递额外数据给页面,在构建时使用
}));
}
const { id } = Astro.params; // 构建时可访问
const { postData } = Astro.props; // 构建时可访问
---
<h1>文章 ID: {id}</h1>
<p>标题: {postData.title}</p>
在 astro build
时,这将生成 dist/posts/1.html
和 dist/posts/2.html
等静态文件。
示例 2:剩余参数 [...slug].astro
(匹配多级路径)
---
// src/pages/docs/[...slug].astro
export async function getStaticPaths() {
return [
{ params: { slug: "getting-started/installation" } }, // 构建时生成 dist/docs/getting-started/installation.html
{ params: { slug: "api/reference" } }, // 构建时生成 dist/docs/api/reference.html
{ params: { slug: undefined } }, // 构建时生成 dist/docs/index.html (顶级页面)
];
}
const { slug } = Astro.params; // 构建时可访问
---
<p>文档路径: {slug || "索引"}</p>
5. 动态路由 (SSR/Hybrid 模式):按需生成
在服务器端渲染 (SSR) 或混合 (Hybrid) 模式下(即 output: 'server'
或 output: 'hybrid'
),这些动态路由页面不会在构建时预先生成所有可能的 HTML 文件。
文件名定义动态参数的方式与 SSG 相同。
无需
getStaticPaths()
函数,页面在服务器接收到请求时按需生成。通过
Astro.params
访问 URL 参数,在请求时动态获取数据并渲染。astro build
会生成服务器端运行所需的代码包,而不是单个的静态 HTML 文件。
示例:src/pages/users/[userId].astro
(SSR)
---
// src/pages/users/[userId].astro
const { userId } = Astro.params; // 请求时可访问
// const userData = await fetchUserFromDB(userId); // 请求时按需获取数据
---
<h1>用户资料: {userId}</h1>
6. 重定向:redirects
和 Astro.redirect()
配置重定向 (在
astro.config.mjs
中,Astro v2.9.0+):在
astro build
时,对于静态输出 (output: 'static'
),Astro 默认会为每个重定向规则生成一个带有meta refresh
标签的 HTML 文件。如果使用支持重定向配置的适配器(例如用于 Netlify, Vercel 等平台的适配器),Astro 会尝试将这些规则写入到部署平台特定的配置文件中(如
_redirects
或vercel.json
),以实现更高效的服务器级重定向。
// astro.config.mjs export default { redirects: { "/old-url": "/new-url", "/products/[id]": "/items/[id]", "/legacy": { status: 302, destination: "/current" } } };
动态重定向 (在
.astro
文件中):主要用于 SSR 或 Hybrid 模式,在请求处理期间根据逻辑动态决定是否重定向。
在纯静态构建模式下,如果一个页面包含
Astro.redirect()
并且该页面被预渲染,那么这个重定向逻辑将在构建时执行一次。如果条件不满足,页面会正常构建;如果条件满足,构建过程可能会因尝试重定向而产生非预期的行为或错误,因为它不是设计为在静态构建时生成重定向HTML文件的。通常,条件性的动态重定向更适合服务器端处理。
--- // 仅在SSR/Hybrid模式下按预期工作 if (!userIsAuthenticated) { // 假设这是一个请求时才能确定的条件 return Astro.redirect("/login"); } ---
7. 重写:Astro.rewrite()
(Astro v4.13.0+)
重写主要用于 SSR 或 Hybrid 模式,在不改变浏览器 URL 的情况下,显示不同路径的内容。
构建时影响:在纯静态构建 (
output: 'static'
) 模式下,Astro.rewrite()
的行为与动态重定向类似。如果一个被预渲染的页面包含Astro.rewrite()
,该逻辑会在构建时执行。如果它尝试重写到一个存在的静态路径,Astro 可能会尝试渲染那个路径的内容到当前URL对应的HTML文件。但这通常更适合服务器端动态处理,以避免构建时的复杂性和潜在问题。方法:
return Astro.rewrite("/internal-path-to-render");
示例:
---
// src/pages/es-variant/about.astro
// 主要用于SSR/Hybrid模式
// 假设我们希望 /es-variant/about 显示 /es/about 的内容
return Astro.rewrite("/es/about");
---
8. 路由优先级顺序 (构建时决策)
当多个规则可能匹配同一个 URL 时,Astro 在执行 astro build
以生成静态站点时,会严格按照以下优先级来决定哪个文件最终用于生成该 URL 路径对应的 HTML 文件:
Astro 预留路由 (
_astro/
等):这些由 Astro 内部管理,用于提供构建资源,优先级最高。路径段更多:例如,
src/pages/blog/specific-post.md
会优先于src/pages/blog/[slug].astro
来构建/blog/specific-post
。静态路由 (无参数):例如,
src/pages/about.astro
会优先于src/pages/[pageName].astro
来构建/about
。命名参数的动态路由:例如,
src/pages/category/[name].astro
优先于src/pages/category/[...everythingElse].astro
。预渲染的动态路由 (SSG):通过
getStaticPaths
生成的路由。端点 (
.js
/.ts
):如果一个.js
或.ts
文件与一个.astro
文件路径冲突,端点文件通常优先处理该路径的请求(尤其是在 SSR 模式下,静态模式下它们通常不直接生成同名 HTML)。基于文件的路由优先于配置中的重定向:如果
src/pages/old-page.astro
存在,那么在astro.config.mjs
中为/old-page
配置的重定向将不会生效,Astro 会构建old-page.astro
的内容。字母顺序:如果以上规则都无法决定,则按文件路径的字母顺序确定哪个文件用于构建。
理解这个优先级对于预测 astro build
的输出结果至关重要,避免了构建出非预期的页面内容。
9. 分页:paginate()
(构建时生成静态分页)
Astro 内置分页功能,非常适合在构建时处理大量数据并生成静态的分页页面。
文件名使用动态参数(通常是
[page]
),例如src/pages/blog/[page].astro
。在
getStaticPaths()
函数中,使用paginate()
辅助函数。astro build
时,此函数会根据提供的数据和pageSize
生成所有必要的分页路由参数和页面数据。
示例:
---
// src/pages/news/[page].astro
export async function getStaticPaths({ paginate }) {
const allNewsItems = [/* 构建时可获取的数据源 */];
// astro build 时,paginate会为每一页生成一个路由对象
return paginate(allNewsItems, { pageSize: 5 });
}
// page 对象在构建时由 paginate 的结果提供
const { page } = Astro.props;
---
<h1>新闻 - 第 {page.currentPage} 页</h1>
<ul>{page.data.map(item => <li>{item.title}</li>)}</ul>
{page.url.prev && <a href={page.url.prev}>上一页</a>}
{page.url.next && <a href={page.url.next}>下一页</a>}
在 astro build
过程中,上述代码会生成如 dist/news/1.html
, dist/news/2.html
等所有分页的静态 HTML 文件。
10. 排除页面:下划线前缀 _
(构建时忽略)
在文件名或目录名前加下划线 _
,是告知 astro build
忽略这些文件和目录,不要将它们作为页面路由进行处理和构建。
这些带有
_
前缀的文件或目录不会被路由识别。在
astro build
后,它们不会出现在最终的dist/
输出目录中(除非它们是通过其他方式如public/
目录或作为导入资源被间接包含)。
示例:
src/pages/_drafts/post.md
:此文件在astro build
时将被忽略,不会生成对应的 HTML 页面。src/pages/blog/_components/MyCard.astro
:此文件不会被构建为/blog/_components/MyCard
页面。它可以安全地作为普通 Astro 组件被其他页面导入和使用,而不会创建不必要的路由。
这个特性对于管理草稿、存放与页面相关的非路由组件或工具函数非常有用,确保它们不会意外地成为构建输出的一部分。