页面与路由 #
一、页面基础 #
1.1 什么是 Astro 页面? #
Astro 页面是位于 src/pages/ 目录下的文件,每个文件自动成为一个可访问的路由。
text
src/pages/
├── index.astro → /
├── about.astro → /about
├── contact.astro → /contact
└── blog/
├── index.astro → /blog
└── post.astro → /blog/post
1.2 页面文件类型 #
Astro 支持多种页面文件类型:
| 文件类型 | 说明 |
|---|---|
.astro |
Astro 组件页面 |
.md |
Markdown 页面 |
.mdx |
MDX 页面(需要集成) |
.html |
纯 HTML 页面 |
.js/.ts |
API 端点 |
二、静态路由 #
2.1 基本路由 #
text
src/pages/
├── index.astro → /
├── about.astro → /about
├── services.astro → /services
└── contact.astro → /contact
2.2 嵌套路由 #
text
src/pages/
├── blog/
│ ├── index.astro → /blog
│ ├── latest.astro → /blog/latest
│ └── popular.astro → /blog/popular
├── docs/
│ ├── index.astro → /docs
│ ├── intro.astro → /docs/intro
│ └── guide.astro → /docs/guide
└── products/
├── index.astro → /products
└── list.astro → /products/list
2.3 页面示例 #
astro
---
// src/pages/about.astro
import Layout from '../layouts/Layout.astro';
---
<Layout title="关于我们">
<h1>关于我们</h1>
<p>这是关于页面的内容。</p>
</Layout>
三、动态路由 #
3.1 基本动态路由 #
使用 [参数名] 创建动态路由:
text
src/pages/
├── blog/
│ └── [slug].astro → /blog/:slug
└── users/
└── [id].astro → /users/:id
3.2 动态路由示例 #
astro
---
// src/pages/blog/[slug].astro
import Layout from '../../layouts/Layout.astro';
// 获取动态参数
const { slug } = Astro.params;
// 根据 slug 获取文章数据
const post = await getPost(slug);
if (!post) {
return Astro.redirect('/404');
}
---
<Layout title={post.title}>
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
</article>
</Layout>
3.3 多个动态参数 #
astro
---
// src/pages/blog/[category]/[slug].astro
const { category, slug } = Astro.params;
// URL: /blog/tech/hello-world
// category = 'tech'
// slug = 'hello-world'
---
<h1>分类: {category}</h1>
<h2>文章: {slug}</h2>
3.4 剩余参数路由 #
使用 [...参数名] 匹配剩余路径:
astro
---
// src/pages/docs/[...path].astro
const { path } = Astro.params;
// URL: /docs/guide/getting-started
// path = 'guide/getting-started'
---
<h1>文档路径: {path}</h1>
四、静态生成(getStaticPaths) #
4.1 基本用法 #
在静态模式下,动态路由需要使用 getStaticPaths 预定义所有路径:
astro
---
// src/pages/blog/[slug].astro
export async function getStaticPaths() {
const posts = await getAllPosts();
return posts.map(post => ({
params: { slug: post.slug },
props: { post },
}));
}
const { post } = Astro.props;
---
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
</article>
4.2 传递 Props #
astro
---
// src/pages/products/[id].astro
export async function getStaticPaths() {
const products = await getProducts();
return products.map(product => ({
params: { id: product.id.toString() },
props: {
product,
relatedProducts: await getRelatedProducts(product.id)
},
}));
}
interface Props {
product: Product;
relatedProducts: Product[];
}
const { product, relatedProducts } = Astro.props;
---
<div>
<h1>{product.name}</h1>
<p>价格: ${product.price}</p>
<h2>相关产品</h2>
{relatedProducts.map(p => (
<div>{p.name}</div>
))}
</div>
4.3 多参数静态路径 #
astro
---
// src/pages/blog/[year]/[month]/[slug].astro
export async function getStaticPaths() {
const posts = await getAllPosts();
return posts.map(post => ({
params: {
year: post.date.getFullYear().toString(),
month: (post.date.getMonth() + 1).toString(),
slug: post.slug,
},
props: { post },
}));
}
const { year, month, slug } = Astro.params;
const { post } = Astro.props;
---
<article>
<h1>{post.title}</h1>
<p>发布于 {year}年{month}月</p>
</article>
4.4 剩余参数静态路径 #
astro
---
// src/pages/docs/[...path].astro
export async function getStaticPaths() {
return [
{ params: { path: 'intro' } },
{ params: { path: 'guide/getting-started' } },
{ params: { path: 'guide/advanced/config' } },
{ params: { path: 'api/reference' } },
];
}
const { path } = Astro.params;
---
<h1>文档: {path}</h1>
五、分页 #
5.1 分页路由 #
使用 [页码] 创建分页:
astro
---
// src/pages/blog/[page].astro
export async function getStaticPaths({ paginate }) {
const posts = await getAllPosts();
return paginate(posts, { pageSize: 10 });
}
const { page } = Astro.props;
---
<div>
{page.data.map(post => (
<article>
<h2>{post.title}</h2>
</article>
))}
<nav>
{page.url.prev && <a href={page.url.prev}>上一页</a>}
<span>第 {page.currentPage} 页</span>
{page.url.next && <a href={page.url.next}>下一页</a>}
</nav>
</div>
5.2 分页对象 #
paginate 函数返回的分页对象:
typescript
interface Page {
data: any[]; // 当前页数据
currentPage: number; // 当前页码
lastPage: number; // 最后一页
url: {
current: string; // 当前页 URL
prev: string | undefined; // 上一页 URL
next: string | undefined; // 下一页 URL
};
total: number; // 总数据量
}
5.3 分页示例 #
astro
---
// src/pages/blog/[page].astro
import Layout from '../../layouts/Layout.astro';
export async function getStaticPaths({ paginate }) {
const posts = await getAllPosts();
return paginate(posts, {
pageSize: 6,
paramName: 'page'
});
}
const { page } = Astro.props;
---
<Layout title={`博客 - 第 ${page.currentPage} 页`}>
<div class="posts">
{page.data.map(post => (
<article class="post">
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</article>
))}
</div>
<nav class="pagination">
{page.url.prev && (
<a href={page.url.prev} class="prev">← 上一页</a>
)}
<span class="page-info">
第 {page.currentPage} / {page.lastPage} 页
</span>
{page.url.next && (
<a href={page.url.next} class="next">下一页 →</a>
)}
</nav>
</Layout>
<style>
.posts {
display: grid;
gap: 2rem;
}
.pagination {
display: flex;
justify-content: space-between;
margin-top: 2rem;
padding-top: 2rem;
border-top: 1px solid #e5e7eb;
}
</style>
六、重定向 #
6.1 编程式重定向 #
astro
---
// src/pages/old-page.astro
return Astro.redirect('/new-page');
---
6.2 条件重定向 #
astro
---
// src/pages/admin.astro
const user = Astro.locals.user;
if (!user) {
return Astro.redirect('/login');
}
---
<h1>管理面板</h1>
6.3 配置重定向 #
在 astro.config.mjs 中配置:
javascript
import { defineConfig } from 'astro/config';
export default defineConfig({
redirects: {
'/old-blog': '/blog',
'/old-blog/[slug]': '/blog/[slug]',
},
});
七、404 页面 #
7.1 创建 404 页面 #
astro
---
// src/pages/404.astro
import Layout from '../layouts/Layout.astro';
---
<Layout title="页面未找到">
<div class="not-found">
<h1>404</h1>
<p>抱歉,您访问的页面不存在。</p>
<a href="/">返回首页</a>
</div>
</Layout>
<style>
.not-found {
text-align: center;
padding: 4rem 2rem;
}
h1 {
font-size: 6rem;
color: #dc2626;
}
</style>
八、路由优先级 #
8.1 优先级规则 #
当多个路由匹配同一 URL 时,Astro 按以下优先级选择:
text
优先级从高到低:
1. 静态路由
/about.astro
2. 动态路由
/blog/[slug].astro
3. 剩余参数路由
/docs/[...path].astro
8.2 示例 #
text
src/pages/
├── blog/
│ ├── index.astro → /blog (静态,优先级最高)
│ ├── [slug].astro → /blog/:slug (动态)
│ └── [...path].astro → /blog/* (剩余参数,优先级最低)
九、路由元数据 #
9.1 获取当前路由信息 #
astro
---
// 任何页面或组件
// 当前 URL
const url = Astro.url;
// 路径名
const pathname = url.pathname;
// 查询参数
const searchParams = url.searchParams;
// 完整 URL
const href = url.href;
---
<p>当前路径: {pathname}</p>
<p>查询参数: {searchParams.toString()}</p>
9.2 当前页面检测 #
astro
---
// src/components/Navigation.astro
const currentPath = Astro.url.pathname;
const navItems = [
{ label: '首页', href: '/' },
{ label: '博客', href: '/blog' },
{ label: '关于', href: '/about' },
];
---
<nav>
{navItems.map(item => (
<a
href={item.href}
class={currentPath === item.href ? 'active' : ''}
>
{item.label}
</a>
))}
</nav>
十、最佳实践 #
10.1 路由组织 #
text
推荐结构:
src/pages/
├── index.astro
├── about.astro
├── blog/
│ ├── index.astro
│ ├── [page].astro
│ └── [slug].astro
├── docs/
│ └── [...path].astro
└── 404.astro
10.2 命名规范 #
text
✅ 好的命名:
├── [slug].astro # 语义化参数名
├── [id].astro # 简洁明了
├── [page].astro # 分页参数
❌ 不好的命名:
├── [param].astro # 不明确
├── [x].astro # 无意义
十一、总结 #
Astro 路由核心要点:
text
┌─────────────────────────────────────────────────────┐
│ 路由核心要点 │
├─────────────────────────────────────────────────────┤
│ │
│ 📁 文件路由 文件路径即 URL 路径 │
│ │
│ 📄 静态路由 固定路径的页面 │
│ │
│ 🔄 动态路由 [参数] 匹配动态路径 │
│ │
│ 📄 静态生成 getStaticPaths 预定义路径 │
│ │
│ 📖 分页 paginate 函数处理分页 │
│ │
│ 🔀 重定向 redirect 方法跳转 │
│ │
└─────────────────────────────────────────────────────┘
下一步,让我们学习 布局组件,掌握页面布局的创建!
最后更新:2026-03-28