查詢內容

本頁內容

EmDash 提供在 Astro 頁面與元件中取得內容的查詢函式,遵循 Astro 的 live content collections 模式,回傳含錯誤處理的結構化結果。

查詢函式

EmDash 匯出兩個主要查詢函式:

函式用途回傳值
getEmDashCollection取得某內容類型的全部項目{ entries, error }
getEmDashEntry依 ID 或 slug 取得單筆{ entry, error, isPreview }

emdash 匯入:

import { getEmDashCollection, getEmDashEntry } from "emdash";

取得全部項目

使用 getEmDashCollection 載入某類型的所有項目:

---
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 後,可用 locale 取得指定語言內容:

const { entries: frenchPosts } = await getEmDashCollection("posts", {
	locale: "fr",
	status: "published",
});

const { entries: localizedPosts } = await getEmDashCollection("posts", {
	locale: Astro.currentLocale,
	status: "published",
});

單筆查詢將 locale 作為第三個引數:

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

省略 locale 時預設使用目前請求的語系地區;若無翻譯則走 遞補鏈

依狀態篩選

僅已發佈或僅草稿:

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

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

限制筆數

const { entries: recentPosts } = await getEmDashCollection("posts", {
	status: "published",
	limit: 5,
});

依分類體系篩選

依分類、標籤或自訂詞彙:

const { entries: newsPosts } = await getEmDashCollection("posts", {
	status: "published",
	where: { category: "news" },
});

const { entries: jsPosts } = await getEmDashCollection("posts", {
	status: "published",
	where: { tag: "javascript" },
});

const { entries: featuredNews } = await getEmDashCollection("posts", {
	status: "published",
	where: { category: ["news", "featured"] },
});

對同一分類欄位提供多個值時,where 使用 OR 邏輯。

錯誤處理

需要可靠度時務必檢查 error

const { entries: posts, error } = await getEmDashCollection("posts");

if (error) {
	console.error("Failed to load posts:", error);
	return new Response("Server error", { status: 500 });
}

取得單筆項目

getEmDashEntry 依 ID 或 slug 載入一筆:

---
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>

回傳型別

interface EntryResult<T> {
	entry: ContentEntry<T> | null;
	error?: Error;
	isPreview: boolean;
}

interface ContentEntry<T> {
	id: string;
	data: T;
	edit: EditProxy;
}

預覽模式

中介軟體會驗證 _preview;查詢函式無需額外參數即可回傳草稿:

---
import { getEmDashEntry } from "emdash";

const { slug } = Astro.params;

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">
    預覽中。此內容尚未發佈。
  </div>
)}

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

視覺化編輯

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

排序

getEmDashCollection 不保證順序,請在範本中排序:

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

const sorted = posts.sort(
	(a, b) => (b.data.publishedAt?.getTime() ?? 0) - (a.data.publishedAt?.getTime() ?? 0),
);
posts.sort((a, b) => a.data.title.localeCompare(b.data.title));
posts.sort((a, b) => (a.data.order ?? 0) - (b.data.order ?? 0));
posts.sort(() => Math.random() - 0.5);

TypeScript

npx emdash types
import { getEmDashCollection, getEmDashEntry } from "emdash";
import type { Post } from "../.emdash/types";

const { entries: posts } = await getEmDashCollection<Post>("posts");
const { entry: post } = await getEmDashEntry<Post>("posts", "my-post");

靜態與伺服器算繪

靜態(預先算繪)

---
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);
---

伺服器算繪

---
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 });
}
---

效能

快取

---
const { entries: posts } = await getEmDashCollection("posts", {
  status: "published",
});

Astro.response.headers.set("Cache-Control", "public, max-age=300");
---

避免重複查詢

---
import { getEmDashCollection } from "emdash";
import PostList from "../components/PostList.astro";
import Sidebar from "../components/Sidebar.astro";

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} />

下一步