Astro 為 EmDash 站台提供頁面、版面配置、元件和伺服器端算繪。本指南介紹目前 EmDash 範本所使用的 Astro 概念,並假設你已經了解 WordPress 主題和 PHP 範本。
對於並非 EmDash 特有的框架功能,請參閱 Astro 文件。
專案結構
Astro 站台為每種檔案指定了明確的目錄。目前的 EmDash 範本使用如下結構:
| WordPress | Astro | 用途 |
|---|---|---|
index.php、single.php、page.php | src/pages/ | URL 路由 |
template-parts/ | src/components/ | 可重複使用的標記 |
header.php 和 footer.php | src/layouts/ | 共用的頁面外殼 |
style.css | src/styles/ | 站台樣式 |
| 外掛程式和資料庫設定 | astro.config.mjs | 整合和伺服器端介接器 |
| 主題設定資料 | seed/seed.json | 集合、選單和範例內容 |
部落格範本使用與其公開 URL 相符的路由目錄:
src/
├── components/
│ └── PostCard.astro
├── layouts/
│ └── Base.astro
├── pages/
│ ├── index.astro
│ ├── pages/
│ │ └── [slug].astro
│ └── posts/
│ ├── index.astro
│ └── [slug].astro
└── live.config.ts
Astro 元件
.astro 元件把伺服器端 TypeScript 和 HTML 範本結合在一起。位於 --- 圍欄之間的程式碼在伺服器端執行。第二個圍欄下面的標記會成為回應的 HTML。
下面的元件在 frontmatter 中宣告 props,並在範本中算繪它們:
---
interface Props {
title: string;
excerpt?: string;
href: string;
}
const { title, excerpt, href } = Astro.props;
---
<article>
<h2><a href={href}>{title}</a></h2>
{excerpt && <p>{excerpt}</p>}
</article>
Astro 會對使用 {value} 算繪的值進行跳脫。匯入、資料庫查詢以及其他伺服器端工作都應放在 frontmatter 中。
範本表達式
Astro 範本在 PHP 範本切換到 <?php ?> 的地方使用大括號。EmDash 範本中最常見的模式是取值、條件和陣列對應:
| 目標 | Astro 語法 |
|---|---|
| 輸出一個值 | {post.data.title} |
| 當值存在時算繪 | {post.data.excerpt && <p>{post.data.excerpt}</p>} |
| 在兩種結果之間選擇 | {posts.length === 0 ? <p>No posts yet.</p> : <PostList />} |
| 算繪清單 | {posts.map((post) => <PostCard title={post.data.title} excerpt={post.data.excerpt} href={"/posts/" + post.id} />)} |
表達式可以使用在 frontmatter 中準備好的變數、Astro.props 中的值,或 EmDash 查詢傳回的資料。Astro 預設會跳脫字串值;對於結構化的富文字,請使用 <PortableText /> 這樣的算繪器,而不是注入 HTML。
Props 和插槽
Props 類似於傳給 get_template_part() 的 $args。它們讓每個輸入都變得明確,並且可以由 TypeScript 檢查。
插槽讓父元件可以把標記傳入元件。預設插槽適合放置頁面內容,而具名插槽則提供額外的插入點:
---
interface Props {
title: string;
}
const { title } = Astro.props;
---
<article>
<h2>{title}</h2>
<slot />
<footer><slot name="footer" /></footer>
</article>
下面的頁面填入了這兩個插槽:
---
import Card from "../components/Card.astro";
---
<Card title="Latest post">
<p>The main card content.</p>
<a slot="footer" href="/posts/latest">Read the post</a>
</Card>
插槽只作用於該元件呼叫的區域範圍。它們的行為不像 WordPress 的 action,後者可以接收在其他位置註冊的回呼。
版面配置
版面配置負責共用的文件結構,WordPress 主題通常會把這部分拆分到 header.php 和 footer.php 中。頁面匯入版面配置,並透過版面配置的插槽傳入自己的內容。
下面的版面配置提供了文件外殼:
---
interface Props {
title: string;
}
const { title } = Astro.props;
---
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>{title}</title>
</head>
<body>
<header><a href="/">My site</a></header>
<main><slot /></main>
</body>
</html>
下面的頁面提供了版面配置的標題和主要內容:
---
import Base from "../layouts/Base.astro";
---
<Base title="Home">
<h1>Latest posts</h1>
</Base>
基於檔案的路由
src/pages/ 中的檔案定義路由。帶方括號的檔名會建立動態路徑區段。
| 檔案 | URL |
|---|---|
src/pages/index.astro | / |
src/pages/posts/index.astro | /posts |
src/pages/posts/[slug].astro | /posts/hello-world |
src/pages/pages/[slug].astro | /pages/about |
在 src/pages/posts/[slug].astro 中,Astro.params.slug 包含來自 URL 的值。有關其餘參數、重新導向和其他路由功能,請閱讀 Astro 路由。
伺服器端算繪
目前的 EmDash 範本在 astro.config.mjs 中使用 output: "server"。因此,頁面可以針對每個請求查詢資料庫,已發布的內容不依賴於重新進行靜態建置。
除非站台有意把 EmDash 當作建置時的資料來源,否則不要在 EmDash 主題的路由中新增 getStaticPaths()。官方提供的主題都是伺服器端算繪的。
有關框架層級的行為,請閱讀 Astro 隨選算繪。
查詢 EmDash 內容
EmDash 用 getEmDashCollection() 和 getEmDashEntry() 封裝了 Astro 的即時內容集合。集合結果包含一個 entries 陣列。單一條目的結果包含 entry,當沒有相符的已發布條目時,它為 null。
下面的彙整頁使用與目前部落格範本相同的排序和識別碼:
---
import { getEmDashCollection } from "emdash";
import Base from "../../layouts/Base.astro";
const { entries: posts, error } = await getEmDashCollection("posts", {
orderBy: { published_at: "desc" },
});
if (error) {
return new Response("Could not load posts", { status: 500 });
}
---
<Base title="Posts">
{posts.map((post) => (
<article>
<h2><a href={`/posts/${post.id}`}>{post.data.title}</a></h2>
{post.data.excerpt && <p>{post.data.excerpt}</p>}
</article>
))}
</Base>
post.id 是 Astro 公開的路由識別碼,通常就是條目的 slug。post.data.id 是資料庫識別碼。當某個 API 需要已儲存的內容 ID(例如分類法或留言輔助函式)時,請使用 data.id。
下面的動態路由根據 URL 中的 slug 查找文章,並算繪它的 Portable Text 欄位:
---
import { decodeSlug, getEmDashEntry } from "emdash";
import { PortableText } from "emdash/ui";
import Base from "../../layouts/Base.astro";
const slug = decodeSlug(Astro.params.slug);
if (!slug) return Astro.rewrite("/404");
const { entry: post, error } = await getEmDashEntry("posts", slug);
if (error) return new Response("Could not load the post", { status: 500 });
if (!post) return Astro.rewrite("/404");
---
<Base title={post.data.title}>
<article>
<h1>{post.data.title}</h1>
<PortableText value={post.data.content} />
</article>
</Base>
繼續學習 Astro
EmDash 範本還使用元件樣式和小型瀏覽器指令碼,但它們是一般的 Astro 功能,而不是 EmDash 的概念。有關範圍限定樣式和全域樣式,請閱讀樣式與 CSS;當元件需要瀏覽器端行為時,請閱讀指令碼與事件處理。