跳转到内容

查询内容

EmDash 提供了查询函数,用于在您的 Astro 页面和组件中检索内容。这些函数遵循 Astro 的 实时内容集合 模式,返回带有错误处理的结构化结果。

EmDash 导出两个主要的查询函数:

函数用途返回类型
getEmDashCollection检索某个内容类型的所有条目{ entries, error }
getEmDashEntry通过 ID 或 slug 检索单个条目{ entry, error, isPreview }

从 emdash 导入它们:

import { getEmDashCollection, getEmDashEntry } from "emdash";

使用 getEmDashCollection 检索某个内容类型的所有条目:

src/pages/posts.astro
---
import { getEmDashCollection } from "emdash";
const { entries: posts, error } = await getEmDashCollection("posts");
if (error) {
console.error("Failed to load posts:", error);
}
---
<ul>
{posts.map((post) => (
<li>{post.data.title}</li>
))}
</ul>

当 启用了 i18n 时,按语言区域过滤以检索特定语言的内容:

// French posts
const { entries: frenchPosts } = await getEmDashCollection("posts", {
locale: "fr",
status: "published",
});
// Use the current request locale
const { entries: localizedPosts } = await getEmDashCollection("posts", {
locale: Astro.currentLocale,
status: "published",
});

对于单个条目,将 locale 作为第三个参数传递:

const { entry: post } = await getEmDashEntry("posts", "my-post", {
locale: Astro.currentLocale,
});

当省略 locale 时,它默认为请求的当前语言区域。如果请求的语言区域没有翻译,则会遵循 回退链。

仅检索已发布或草稿内容:

// Only published posts
const { entries: published } = await getEmDashCollection("posts", {
status: "published",
});
// Only drafts
const { entries: drafts } = await getEmDashCollection("posts", {
status: "draft",
});

限制返回的条目数量:

// Get the 5 most recent posts
const { entries: recentPosts } = await getEmDashCollection("posts", {
status: "published",
limit: 5,
});

按类别、标签或自定义分类法术语过滤条目:

// Posts in the "news" category
const { entries: newsPosts } = await getEmDashCollection("posts", {
status: "published",
where: { category: "news" },
});
// Posts with the "javascript" tag
const { entries: jsPosts } = await getEmDashCollection("posts", {
status: "published",
where: { tag: "javascript" },
});
// Posts matching any of multiple terms
const { entries: featuredNews } = await getEmDashCollection("posts", {
status: "published",
where: { category: ["news", "featured"] },
});

当为单个分类法提供多个值时,where 过滤器使用 OR 逻辑。

当可靠性很重要时,请务必检查错误:

const { entries: posts, error } = await getEmDashCollection("posts");
if (error) {
// Log and handle gracefully
console.error("Failed to load posts:", error);
return new Response("Server error", { status: 500 });
}

使用 getEmDashEntry 通过其 ID 或 slug 检索一个条目:

src/pages/posts/[slug].astro
---
import { getEmDashEntry } from "emdash";
import { PortableText } from "emdash/ui";
const { slug } = Astro.params;
const { entry: post, error } = await getEmDashEntry("posts", slug);
if (error) {
return new Response("Server error", { status: 500 });
}
if (!post) {
return Astro.redirect("/404");
}
---
<article>
<h1>{post.data.title}</h1>
<PortableText value={post.data.content} />
</article>

getEmDashEntry 返回一个结果对象:

interface EntryResult<T> {
entry: ContentEntry<T> | null; // null if not found
error?: Error; // Only set for actual errors (not "not found")
isPreview: boolean; // true if viewing preview/draft content
}
interface ContentEntry<T> {
id: string;
data: T;
edit: EditProxy; // Visual editing annotations
}

entry 内的 data 对象包含为该内容类型定义的所有字段。edit 代理提供可视化编辑注解(见下文)。

EmDash 通过中间件自动处理预览。当 URL 包含有效的 _preview 令牌时,中间件会验证它并设置请求上下文。然后,您的查询函数无需任何特殊参数即可提供草稿内容:

src/pages/posts/[...slug].astro
---
import { getEmDashEntry } from "emdash";
const { slug } = Astro.params;
// No special preview handling needed — middleware does it automatically
const { entry, isPreview, error } = await getEmDashEntry("posts", slug);
if (error) {
return new Response("Server error", { status: 500 });
}
if (!entry) {
return Astro.redirect("/404");
}
---
{isPreview && (
<div class="preview-banner">
Viewing preview. This content is not published.
</div>
)}
<article>
<h1>{entry.data.title}</h1>
<PortableText value={entry.data.content} />
</article>

查询函数返回的每个条目都包含一个 edit 代理,用于注解您的模板。将其展开到元素上,以便为经过身份验证的编辑者启用内联编辑:

<article {...entry.edit}>
<h1 {...entry.edit.title}>{entry.data.title}</h1>
<div {...entry.edit.content}>
<PortableText value={entry.data.content} />
</div>
</article>

在编辑模式下,{...entry.edit.title} 会生成一个 data-emdash-ref 属性,可视化编辑工具栏使用该属性来启用内联编辑。在生产环境中,代理展开不会产生任何输出——零运行时成本。

getEmDashCollection 不保证排序顺序。在您的模板中对结果进行排序:

const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
});
// Sort by publication date, newest first
const sorted = posts.sort(
(a, b) => (b.data.publishedAt?.getTime() ?? 0) - (a.data.publishedAt?.getTime() ?? 0),
);
// Alphabetical by title
posts.sort((a, b) => a.data.title.localeCompare(b.data.title));
// By custom order field
posts.sort((a, b) => (a.data.order ?? 0) - (b.data.order ?? 0));
// Random order
posts.sort(() => Math.random() - 0.5);

为您的集合生成 TypeScript 类型:

Terminal window
npx emdash types

这将创建 .emdash/types.ts,其中包含每个集合的接口。使用它们以获得类型安全:

import { getEmDashCollection, getEmDashEntry } from "emdash";
import type { Post } from "../.emdash/types";
// Type-safe collection query
const { entries: posts } = await getEmDashCollection<Post>("posts");
// posts is ContentEntry<Post>[]
// Type-safe entry query
const { entry: post } = await getEmDashEntry<Post>("posts", "my-post");
// post is ContentEntry<Post> | null

EmDash 内容适用于静态渲染和服务器渲染的页面。

对于静态页面,使用 getStaticPaths 在构建时生成路由:

src/pages/posts/[slug].astro
---
import { getEmDashCollection, getEmDashEntry } from "emdash";
export async function getStaticPaths() {
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
});
return posts.map((post) => ({
params: { slug: post.data.slug },
}));
}
const { slug } = Astro.params;
const { entry: post } = await getEmDashEntry("posts", slug);
---

对于服务器渲染的页面,直接查询内容:

src/pages/posts/[slug].astro
---
export const prerender = false;
import { getEmDashEntry } from "emdash";
const { slug } = Astro.params;
const { entry: post, error } = await getEmDashEntry("posts", slug);
if (error) {
return new Response("Server error", { status: 500 });
}
if (!post) {
return new Response(null, { status: 404 });
}
---

EmDash 使用 Astro 的实时内容集合,它会自动处理缓存。对于服务器渲染的页面,请考虑添加 HTTP 缓存头:

---
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
});
// Cache for 5 minutes
Astro.response.headers.set("Cache-Control", "public, max-age=300");
---

查询一次并将数据传递给组件:

src/pages/index.astro
---
import { getEmDashCollection } from "emdash";
import PostList from "../components/PostList.astro";
import Sidebar from "../components/Sidebar.astro";
// Query once
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
});
const featured = posts.filter((p) => p.data.featured);
const recent = posts.slice(0, 5);
---
<PostList posts={featured} />
<Sidebar posts={recent} />