静态数据 #
一、本地 JSON 数据 #
1.1 创建 JSON 文件 #
json
// src/data/products.json
[
{
"id": 1,
"name": "无线耳机",
"price": 299,
"category": "电子产品",
"image": "/images/headphones.jpg",
"description": "高品质无线蓝牙耳机"
},
{
"id": 2,
"name": "机械键盘",
"price": 599,
"category": "电子产品",
"image": "/images/keyboard.jpg",
"description": "RGB背光机械键盘"
},
{
"id": 3,
"name": "显示器支架",
"price": 199,
"category": "配件",
"image": "/images/stand.jpg",
"description": "可调节显示器支架"
}
]
1.2 导入 JSON 数据 #
astro
---
// src/pages/products.astro
import products from '../data/products.json';
---
<div class="products">
{products.map(product => (
<div key={product.id} class="product-card">
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p class="category">{product.category}</p>
<p class="price">¥{product.price}</p>
<p class="description">{product.description}</p>
</div>
))}
</div>
1.3 TypeScript 类型定义 #
typescript
// src/types/product.ts
interface Product {
id: number;
name: string;
price: number;
category: string;
image: string;
description: string;
}
export type { Product };
astro
---
// src/pages/products.astro
import products from '../data/products.json';
import type { Product } from '../types/product';
const typedProducts: Product[] = products;
---
<div>
{typedProducts.map(product => (
<div key={product.id}>{product.name}</div>
))}
</div>
二、Glob 导入 #
2.1 基本用法 #
使用 Astro.glob() 批量导入文件:
astro
---
// src/pages/blog/index.astro
const posts = await Astro.glob('../content/blog/*.md');
---
<div>
{posts.map(post => (
<article>
<h2>{post.frontmatter.title}</h2>
<p>{post.frontmatter.excerpt}</p>
</article>
))}
</div>
2.2 Glob 导入 JSON 文件 #
text
src/data/products/
├── headphones.json
├── keyboard.json
└── stand.json
astro
---
// src/pages/products.astro
const productFiles = await Astro.glob('../data/products/*.json');
const products = productFiles.map(file => file.default);
---
<div>
{products.map(product => (
<div key={product.id}>
<h3>{product.name}</h3>
<p>¥{product.price}</p>
</div>
))}
</div>
2.3 Glob 导入 Markdown #
text
src/content/blog/
├── hello-world.md
├── astro-guide.md
└── web-development.md
astro
---
// src/pages/blog/index.astro
const posts = await Astro.glob('../content/blog/*.md');
---
<div class="blog-list">
{posts
.sort((a, b) =>
new Date(b.frontmatter.date).getTime() -
new Date(a.frontmatter.date).getTime()
)
.map(post => (
<article class="post">
<time datetime={post.frontmatter.date}>
{new Date(post.frontmatter.date).toLocaleDateString('zh-CN')}
</time>
<h2>
<a href={`/blog/${post.frontmatter.slug || post.file.split('/').pop()?.replace('.md', '')}`}>
{post.frontmatter.title}
</a>
</h2>
<p>{post.frontmatter.excerpt}</p>
<div class="tags">
{post.frontmatter.tags?.map((tag: string) => (
<span class="tag">{tag}</span>
))}
</div>
</article>
))
}
</div>
2.4 获取 Markdown 内容 #
astro
---
// src/pages/blog/[slug].astro
const posts = await Astro.glob('../content/blog/*.md');
export function getStaticPaths() {
return posts.map(post => ({
params: {
slug: post.frontmatter.slug ||
post.file.split('/').pop()?.replace('.md', '')
},
props: { post },
}));
}
const { post } = Astro.props;
const { Content } = await post;
---
<article>
<header>
<h1>{post.frontmatter.title}</h1>
<time>{post.frontmatter.date}</time>
</header>
<Content />
</article>
三、Markdown Frontmatter #
3.1 Frontmatter 格式 #
markdown
---
title: Astro
date: 2024-01-15
excerpt: 学习 Astro 的基础知识
tags:
- Astro
- 前端
author: 张三
cover: /images/astro-guide.jpg
draft: false
---
# Astro 入门指南
这是文章内容...
3.2 类型安全的 Frontmatter #
typescript
// src/types/blog.ts
interface BlogFrontmatter {
title: string;
date: string;
excerpt: string;
tags?: string[];
author?: string;
cover?: string;
draft?: boolean;
}
interface BlogPost {
frontmatter: BlogFrontmatter;
file: string;
url: string;
Content: any;
}
export type { BlogFrontmatter, BlogPost };
3.3 使用类型化数据 #
astro
---
// src/pages/blog/index.astro
import type { BlogPost } from '../types/blog';
const posts = await Astro.glob<BlogFrontmatter>('../content/blog/*.md');
const publishedPosts = posts.filter(post => !post.frontmatter.draft);
---
<div>
{publishedPosts.map(post => (
<article key={post.file}>
<h2>{post.frontmatter.title}</h2>
<p>{post.frontmatter.excerpt}</p>
</article>
))}
</div>
四、数据目录组织 #
4.1 推荐目录结构 #
text
src/
├── data/
│ ├── json/
│ │ ├── products.json
│ │ ├── categories.json
│ │ └── authors.json
│ ├── yaml/
│ │ ├── config.yaml
│ │ └── navigation.yaml
│ └── index.ts
├── content/
│ ├── blog/
│ │ ├── post-1.md
│ │ └── post-2.md
│ └── docs/
│ ├── intro.md
│ └── guide.md
└── types/
├── product.ts
└── blog.ts
4.2 数据导出索引 #
typescript
// src/data/index.ts
import products from './json/products.json';
import categories from './json/categories.json';
import authors from './json/authors.json';
export { products, categories, authors };
export type { Product, Category, Author } from '../types';
4.3 使用数据索引 #
astro
---
// src/pages/products.astro
import { products, categories } from '../data';
---
<div>
<aside>
<h2>分类</h2>
<ul>
{categories.map(cat => (
<li key={cat.id}>{cat.name}</li>
))}
</ul>
</aside>
<main>
<h2>产品</h2>
{products.map(product => (
<div key={product.id}>{product.name}</div>
))}
</main>
</div>
五、YAML 数据 #
5.1 安装 YAML 解析器 #
bash
npm install yaml
5.2 YAML 文件示例 #
yaml
# src/data/yaml/navigation.yaml
main:
- label: 首页
href: /
- label: 产品
href: /products
children:
- label: 产品A
href: /products/a
- label: 产品B
href: /products/b
- label: 博客
href: /blog
- label: 关于
href: /about
footer:
- label: 隐私政策
href: /privacy
- label: 服务条款
href: /terms
5.3 导入 YAML 数据 #
astro
---
// src/components/Navigation.astro
import { parse } from 'yaml';
import navYaml from '../data/yaml/navigation.yaml?raw';
const navigation = parse(navYaml);
---
<nav>
<ul>
{navigation.main.map(item => (
<li key={item.href}>
<a href={item.href}>{item.label}</a>
{item.children && (
<ul>
{item.children.map(child => (
<li key={child.href}>
<a href={child.href}>{child.label}</a>
</li>
))}
</ul>
)}
</li>
))}
</ul>
</nav>
六、CSV 数据 #
6.1 安装 CSV 解析器 #
bash
npm install csv-parse
6.2 CSV 文件示例 #
csv
// src/data/csv/users.csv
id,name,email,role
1,张三,zhang@example.com,admin
2,李四,li@example.com,user
3,王五,wang@example.com,user
6.3 解析 CSV 数据 #
astro
---
// src/pages/users.astro
import { parse } from 'csv-parse/sync';
import usersCsv from '../data/csv/users.csv?raw';
interface User {
id: string;
name: string;
email: string;
role: string;
}
const users: User[] = parse(usersCsv, {
columns: true,
skip_empty_lines: true,
});
---
<table>
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>邮箱</th>
<th>角色</th>
</tr>
</thead>
<tbody>
{users.map(user => (
<tr key={user.id}>
<td>{user.id}</td>
<td>{user.name}</td>
<td>{user.email}</td>
<td>{user.role}</td>
</tr>
))}
</tbody>
</table>
七、数据转换 #
7.1 数据过滤 #
astro
---
import { products } from '../data';
const featuredProducts = products.filter(p => p.featured);
const affordableProducts = products.filter(p => p.price < 500);
---
<div>
<h2>精选产品</h2>
{featuredProducts.map(p => <div key={p.id}>{p.name}</div>)}
<h2>平价产品</h2>
{affordableProducts.map(p => <div key={p.id}>{p.name}</div>)}
</div>
7.2 数据排序 #
astro
---
import { products } from '../data';
const sortedByPrice = [...products].sort((a, b) => a.price - b.price);
const sortedByName = [...products].sort((a, b) => a.name.localeCompare(b.name));
---
<div>
<h2>按价格排序</h2>
{sortedByPrice.map(p => <div key={p.id}>{p.name} - ¥{p.price}</div>)}
</div>
7.3 数据分组 #
astro
---
import { products } from '../data';
const groupedByCategory = products.reduce((acc, product) => {
const { category } = product;
if (!acc[category]) {
acc[category] = [];
}
acc[category].push(product);
return acc;
}, {} as Record<string, typeof products>);
---
<div>
{Object.entries(groupedByCategory).map(([category, categoryProducts]) => (
<section key={category}>
<h2>{category}</h2>
{categoryProducts.map(product => (
<div key={product.id}>{product.name}</div>
))}
</section>
))}
</div>
八、数据验证 #
8.1 使用 Zod 验证 #
bash
npm install zod
typescript
// src/schemas/product.ts
import { z } from 'zod';
const ProductSchema = z.object({
id: z.number(),
name: z.string().min(1),
price: z.number().positive(),
category: z.string(),
image: z.string().url(),
description: z.string(),
featured: z.boolean().optional(),
});
export { ProductSchema };
8.2 验证数据 #
astro
---
import { products } from '../data';
import { ProductSchema } from '../schemas/product';
const validatedProducts = products.map(product => {
const result = ProductSchema.safeParse(product);
if (!result.success) {
console.error(`Invalid product: ${product.id}`, result.error);
return null;
}
return result.data;
}).filter(Boolean);
---
<div>
{validatedProducts.map(product => (
<div key={product.id}>{product.name}</div>
))}
</div>
九、总结 #
静态数据核心要点:
text
┌─────────────────────────────────────────────────────┐
│ 静态数据要点 │
├─────────────────────────────────────────────────────┤
│ │
│ 📄 JSON 直接导入 JSON 文件 │
│ │
│ 📦 Glob 批量导入多个文件 │
│ │
│ 📝 Markdown Frontmatter + 内容 │
│ │
│ 📋 YAML 结构化配置数据 │
│ │
│ 📊 CSV 表格数据导入 │
│ │
│ ✅ 验证 使用 Zod 进行数据验证 │
│ │
└─────────────────────────────────────────────────────┘
下一步,让我们学习 动态数据,了解 SSR 模式下的数据处理!
最后更新:2026-03-28