查询数据 #
一、基本查询 #
1.1 getCollection #
获取集合中的所有条目:
astro
---
import { getCollection } from 'astro:content';
const posts = await getCollection('blog');
---
<div>
{posts.map(post => (
<article key={post.slug}>
<h2>{post.data.title}</h2>
</article>
))}
</div>
1.2 getEntry #
获取单个条目:
astro
---
import { getEntry } from 'astro:content';
const post = await getEntry('blog', 'hello-world');
---
<article>
<h1>{post.data.title}</h1>
<p>{post.data.description}</p>
</article>
1.3 getEntries #
获取多个条目:
astro
---
import { getEntries } from 'astro:content';
const posts = await getEntries([
{ collection: 'blog', slug: 'hello-world' },
{ collection: 'blog', slug: 'astro-guide' },
]);
---
<div>
{posts.map(post => (
<article key={post.slug}>{post.data.title}</article>
))}
</div>
二、过滤查询 #
2.1 基本过滤 #
astro
---
import { getCollection } from 'astro:content';
const posts = await getCollection('blog', ({ data }) => {
return !data.draft;
});
---
<div>
{posts.map(post => (
<article key={post.slug}>{post.data.title}</article>
))}
</div>
2.2 多条件过滤 #
astro
---
const posts = await getCollection('blog', ({ data }) => {
return !data.draft && data.featured;
});
---
<div>
{posts.map(post => (
<article key={post.slug}>
<span class="badge">精选</span>
<h2>{post.data.title}</h2>
</article>
))}
</div>
2.3 日期过滤 #
astro
---
const now = new Date();
const currentYear = now.getFullYear();
const posts = await getCollection('blog', ({ data }) => {
const postYear = data.date.getFullYear();
return !data.draft && postYear === currentYear;
});
---
<div>
<h2>{currentYear}年的文章</h2>
{posts.map(post => (
<article key={post.slug}>{post.data.title}</article>
))}
</div>
2.4 标签过滤 #
astro
---
const tag = Astro.url.searchParams.get('tag') || '';
const posts = await getCollection('blog', ({ data }) => {
if (!tag) return !data.draft;
return !data.draft && data.tags.includes(tag);
});
---
<div>
{tag && <p>标签: {tag}</p>}
{posts.map(post => (
<article key={post.slug}>{post.data.title}</article>
))}
</div>
2.5 分类过滤 #
astro
---
const category = Astro.params.category;
const posts = await getCollection('blog', ({ data }) => {
return !data.draft && data.categories.includes(category);
});
---
<div>
<h1>分类: {category}</h1>
{posts.map(post => (
<article key={post.slug}>{post.data.title}</article>
))}
</div>
三、排序 #
3.1 按日期排序 #
astro
---
import { getCollection } from 'astro:content';
const posts = (await getCollection('blog'))
.filter(post => !post.data.draft)
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
---
<div>
{posts.map(post => (
<article key={post.slug}>
<time>{post.data.date.toLocaleDateString('zh-CN')}</time>
<h2>{post.data.title}</h2>
</article>
))}
</div>
3.2 按标题排序 #
astro
---
const posts = (await getCollection('blog'))
.filter(post => !post.data.draft)
.sort((a, b) => a.data.title.localeCompare(b.data.title, 'zh-CN'));
---
<div>
{posts.map(post => (
<article key={post.slug}>{post.data.title}</article>
))}
</div>
3.3 按自定义字段排序 #
astro
---
const docs = (await getCollection('docs'))
.sort((a, b) => a.data.order - b.data.order);
---
<div>
{docs.map(doc => (
<article key={doc.slug}>
{doc.data.order}. {doc.data.title}
</article>
))}
</div>
3.4 多字段排序 #
astro
---
const posts = (await getCollection('blog'))
.filter(post => !post.data.draft)
.sort((a, b) => {
if (a.data.featured !== b.data.featured) {
return b.data.featured ? 1 : -1;
}
return b.data.date.valueOf() - a.data.date.valueOf();
});
---
<div>
{posts.map(post => (
<article key={post.slug}>
{post.data.featured && <span class="badge">精选</span>}
<h2>{post.data.title}</h2>
</article>
))}
</div>
四、分页 #
4.1 手动分页 #
astro
---
import { getCollection } from 'astro:content';
const page = parseInt(Astro.url.searchParams.get('page') || '1');
const pageSize = 10;
const allPosts = (await getCollection('blog'))
.filter(post => !post.data.draft)
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
const totalPages = Math.ceil(allPosts.length / pageSize);
const startIndex = (page - 1) * pageSize;
const posts = allPosts.slice(startIndex, startIndex + pageSize);
---
<div>
{posts.map(post => (
<article key={post.slug}>{post.data.title}</article>
))}
<nav class="pagination">
{page > 1 && (
<a href={`?page=${page - 1}`}>上一页</a>
)}
<span>第 {page} / {totalPages} 页</span>
{page < totalPages && (
<a href={`?page=${page + 1}`}>下一页</a>
)}
</nav>
</div>
4.2 使用 Astro 分页 #
astro
---
// src/pages/blog/[page].astro
export async function getStaticPaths({ paginate }) {
const posts = (await getCollection('blog'))
.filter(post => !post.data.draft)
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
return paginate(posts, { pageSize: 10 });
}
const { page } = Astro.props;
---
<div>
{page.data.map(post => (
<article key={post.slug}>
<h2>{post.data.title}</h2>
<time>{post.data.date.toLocaleDateString('zh-CN')}</time>
</article>
))}
<nav class="pagination">
{page.url.prev && (
<a href={page.url.prev}>上一页</a>
)}
<span>第 {page.currentPage} / {page.lastPage} 页</span>
{page.url.next && (
<a href={page.url.next}>下一页</a>
)}
</nav>
</div>
五、分组 #
5.1 按年份分组 #
astro
---
import { getCollection } from 'astro:content';
const posts = (await getCollection('blog'))
.filter(post => !post.data.draft)
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
const groupedByYear = posts.reduce((acc, post) => {
const year = post.data.date.getFullYear();
if (!acc[year]) acc[year] = [];
acc[year].push(post);
return acc;
}, {} as Record<number, typeof posts>);
---
<div>
{Object.entries(groupedByYear)
.sort(([a], [b]) => Number(b) - Number(a))
.map(([year, yearPosts]) => (
<section key={year}>
<h2>{year}年</h2>
{yearPosts.map(post => (
<article key={post.slug}>
<h3>{post.data.title}</h3>
</article>
))}
</section>
))
}
</div>
5.2 按分类分组 #
astro
---
const posts = await getCollection('blog');
const groupedByCategory = posts.reduce((acc, post) => {
post.data.categories.forEach(category => {
if (!acc[category]) acc[category] = [];
acc[category].push(post);
});
return acc;
}, {} as Record<string, typeof posts>);
---
<div>
{Object.entries(groupedByCategory).map(([category, categoryPosts]) => (
<section key={category}>
<h2>{category}</h2>
{categoryPosts.map(post => (
<article key={post.slug}>{post.data.title}</article>
))}
</section>
))}
</div>
5.3 按标签分组 #
astro
---
const posts = await getCollection('blog');
const tags = posts.reduce((acc, post) => {
post.data.tags.forEach(tag => {
if (!acc[tag]) acc[tag] = 0;
acc[tag]++;
});
return acc;
}, {} as Record<string, number>);
---
<div class="tags">
{Object.entries(tags)
.sort(([, a], [, b]) => b - a)
.map(([tag, count]) => (
<a href={`/tags/${tag}`} class="tag">
{tag} ({count})
</a>
))
}
</div>
六、关联查询 #
6.1 作者关联 #
astro
---
import { getCollection, getEntry } from 'astro:content';
const posts = await getCollection('blog');
async function getPostWithAuthor(post) {
const author = await getEntry('authors', post.data.author);
return { post, author };
}
const postsWithAuthors = await Promise.all(
posts.map(getPostWithAuthor)
);
---
<div>
{postsWithAuthors.map(({ post, author }) => (
<article key={post.slug}>
<h2>{post.data.title}</h2>
<p>作者: {author.data.name}</p>
</article>
))}
</div>
6.2 相关文章 #
astro
---
import { getCollection, getEntry } from 'astro:content';
const currentPost = await getEntry('blog', Astro.params.slug);
const allPosts = await getCollection('blog');
const relatedPosts = allPosts
.filter(post =>
post.slug !== currentPost.slug &&
!post.data.draft &&
post.data.tags.some(tag => currentPost.data.tags.includes(tag))
)
.sort((a, b) => {
const aCommonTags = a.data.tags.filter(t => currentPost.data.tags.includes(t)).length;
const bCommonTags = b.data.tags.filter(t => currentPost.data.tags.includes(t)).length;
return bCommonTags - aCommonTags;
})
.slice(0, 3);
---
<aside>
<h3>相关文章</h3>
{relatedPosts.map(post => (
<a key={post.slug} href={`/blog/${post.slug}`}>
{post.data.title}
</a>
))}
</aside>
七、搜索功能 #
7.1 标题搜索 #
astro
---
const query = Astro.url.searchParams.get('q')?.toLowerCase() || '';
const posts = (await getCollection('blog'))
.filter(post =>
!post.data.draft &&
post.data.title.toLowerCase().includes(query)
);
---
<div>
<form>
<input type="search" name="q" value={query} placeholder="搜索文章..." />
<button type="submit">搜索</button>
</form>
{query && (
<p>搜索 "{query}" 找到 {posts.length} 篇文章</p>
)}
{posts.map(post => (
<article key={post.slug}>{post.data.title}</article>
))}
</div>
7.2 全文搜索 #
astro
---
const query = Astro.url.searchParams.get('q')?.toLowerCase() || '';
const posts = (await getCollection('blog'))
.filter(post => {
if (post.data.draft) return false;
if (!query) return true;
const searchContent = [
post.data.title,
post.data.description,
post.body,
].join(' ').toLowerCase();
return searchContent.includes(query);
});
---
<div>
{posts.map(post => (
<article key={post.slug}>
<h2>{post.data.title}</h2>
<p>{post.data.description}</p>
</article>
))}
</div>
八、缓存和性能 #
8.1 预加载数据 #
typescript
// src/lib/content.ts
import { getCollection } from 'astro:content';
let cachedPosts: Awaited<ReturnType<typeof getCollection>> | null = null;
export async function getPosts() {
if (!cachedPosts) {
cachedPosts = await getCollection('blog');
}
return cachedPosts;
}
8.2 使用预加载数据 #
astro
---
import { getPosts } from '../lib/content';
const posts = await getPosts();
---
<div>
{posts.map(post => (
<article key={post.slug}>{post.data.title}</article>
))}
</div>
九、总结 #
查询数据核心要点:
text
┌─────────────────────────────────────────────────────┐
│ 查询数据要点 │
├─────────────────────────────────────────────────────┤
│ │
│ 📋 getCollection 获取集合所有条目 │
│ │
│ 📄 getEntry 获取单个条目 │
│ │
│ 🔍 过滤 条件筛选数据 │
│ │
│ 📊 排序 按字段排序 │
│ │
│ 📖 分页 分页显示数据 │
│ │
│ 📦 分组 按条件分组 │
│ │
│ 🔗 关联 关联查询 │
│ │
└─────────────────────────────────────────────────────┘
下一步,让我们学习 内容渲染,掌握内容渲染技术!
最后更新:2026-03-28