静态数据 #

一、本地 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