Astro 是一個用於構建內容驅動網站的 Web 框架。使用 EmDash 時,Astro 取代了您的 WordPress 主題 — 它處理模板、路由和渲染。
本指南透過將 Astro 基礎知識映射到您已經理解的 WordPress 概念來進行教學。
關鍵範式轉變
預設伺服器渲染
像 PHP 一樣,Astro 程式碼在伺服器上執行。與 PHP 不同的是,它預設輸出零 JavaScript 的靜態 HTML。
除非新增否則零 JS
WordPress 自動載入 jQuery 和主題指令碼。Astro 不會向瀏覽器傳送任何內容, 除非您明確新增它。
基於元件的架構
不再使用分散的模板標籤和包含檔案,而是使用可組合的、自包含的 元件進行構建。
基於檔案的路由
沒有重寫規則或 query_vars。src/pages/ 中的檔案結構直接定義您的 URL。
專案結構
WordPress 主題具有扁平結構和魔術檔名。Astro 使用明確的目錄:
| WordPress | Astro | 用途 |
|---|---|---|
index.php、single.php | src/pages/ | 路由(URL) |
template-parts/ | src/components/ | 可重用的 UI 元件 |
header.php + footer.php | src/layouts/ | 頁面包裝器 |
style.css | src/styles/ | 全域 CSS |
functions.php | astro.config.mjs | 網站設定 |
以下樹顯示了典型的 Astro 專案佈局:
src/
├── components/ # Reusable UI (Header, PostCard, etc.)
├── layouts/ # Page shells (Base.astro)
├── pages/ # Routes - files become URLs
│ ├── index.astro # → /
│ ├── posts/
│ │ ├── index.astro # → /posts
│ │ └── [slug].astro # → /posts/hello-world
│ └── [slug].astro # → /about, /contact, etc.
└── styles/
└── global.css
Astro 元件
.astro 檔案是 Astro 相當於 PHP 模板的東西。每個檔案有兩部分:
- Frontmatter(在
---圍欄之間)— 伺服器端程式碼,類似於模板頂部的 PHP - 模板 — 帶有表示式的 HTML,類似於 PHP 模板的其餘部分
以下元件在 frontmatter 中宣告型別化的 props 並在模板中渲染它們:
---
// Frontmatter: runs on server, never sent to browser
interface Props {
title: string;
excerpt: string;
url: string;
}
const { title, excerpt, url } = Astro.props;
---
<!-- Template: outputs HTML -->
<article class="post-card">
<h2><a href={url}>{title}</a></h2>
<p>{excerpt}</p>
</article>
與 PHP 的主要區別:
- Frontmatter 是隔離的。 在那裡宣告的變數在模板中可用,但程式碼本身永遠不會到達瀏覽器。
- 匯入放在 frontmatter 中。 元件、資料、工具 — 全部在頂部匯入。
- TypeScript 有效。 使用
interface Props定義 prop 型別,以獲得編輯器自動完成和驗證。
模板表示式
Astro 模板使用 {花括號} 而不是 <?php ?> 標籤。語法類似於 JSX,但輸出純 HTML。
Astro
---
import { getEmDashCollection } from "emdash";
const { entries: posts } = await getEmDashCollection("posts");
const showTitle = true;
---
{showTitle && <h1>Latest Posts</h1>}
{posts.length > 0 ? (
<ul>
{posts.map(post => (
<li>
<a href={`/posts/${post.id}`}>{post.data.title}</a>
</li>
))}
</ul>
) : (
<p>No posts found.</p>
)} PHP
<?php
$posts = new WP_Query(['post_type' => 'post']);
$show_title = true;
?>
<?php if ($show_title): ?>
<h1>Latest Posts</h1>
<?php endif; ?>
<?php if ($posts->have_posts()): ?>
<ul>
<?php while ($posts->have_posts()): $posts->the_post(); ?>
<li>
<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
</li>
<?php endwhile; wp_reset_postdata(); ?>
</ul>
<?php else: ?>
<p>No posts found.</p>
<?php endif; ?> 表示式模式
| 模式 | 用途 |
|---|---|
{variable} | 輸出值 |
{condition && <Element />} | 條件渲染 |
{condition ? <A /> : <B />} | If/else |
{items.map(item => <Li>{item}</Li>)} | 迴圈 |
Props 和 Slots
元件透過 props(類似函式引數)和 slots(類似 do_action 插入點)接收資料。
Astro
---
interface Props {
title: string;
featured?: boolean;
}
const { title, featured = false } = Astro.props;
---
<article class:list={["card", { featured }]}>
<h2>{title}</h2>
<slot />
<slot name="footer" />
</article>以下標記使用該元件,傳遞預設插槽和命名的 footer 插槽:
<Card title="Hello" featured>
<p>This goes in the default slot.</p>
<footer slot="footer">Footer content</footer>
</Card> PHP
<?php
// Usage: get_template_part('template-parts/card', null, [
// 'title' => 'Hello',
// 'featured' => true
// ]);
$title = $args['title'] ?? '';
$featured = $args['featured'] ?? false;
$class = $featured ? 'card featured' : 'card';
?>
<article class="<?php echo esc_attr($class); ?>">
<h2><?php echo esc_html($title); ?></h2>
<?php
// No direct equivalent to slots.
// WordPress uses do_action() for similar patterns:
do_action('card_content');
do_action('card_footer');
?>
</article> Props vs $args
在 WordPress 中,get_template_part() 透過 $args 陣列傳遞資料。Astro props 是型別化的並解構的:
---
// Type-safe with defaults
interface Props {
title: string;
count?: number;
}
const { title, count = 10 } = Astro.props;
---
Slots vs Hooks
WordPress 使用 do_action() 建立插入點。Astro 使用插槽:
| WordPress | Astro |
|---|---|
do_action('before_content') | <slot name="before" /> |
| 預設內容區域 | <slot /> |
do_action('after_content') | <slot name="after" /> |
區別:插槽在呼叫位置接收子元素,而 WordPress 鉤子需要在別處單獨的 add_action() 呼叫。
佈局
佈局用公共 HTML 結構包裝頁面 — <head>、頭部、頁尾以及頁面之間共享的所有內容。這取代了 header.php + footer.php。以下佈局定義了共享外殼並公開了頁面內容的插槽:
---
import "../styles/global.css";
interface Props {
title: string;
description?: string;
}
const { title, description = "My EmDash Site" } = Astro.props;
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content={description} />
<title>{title}</title>
</head>
<body>
<header>
<nav><!-- Navigation --></nav>
</header>
<main>
<slot />
</main>
<footer>
<p>© {new Date().getFullYear()}</p>
</footer>
</body>
</html>
在頁面中使用佈局:
---
import Base from "../layouts/Base.astro";
---
<Base title="Home">
<h1>Welcome</h1>
<p>Page content goes in the slot.</p>
</Base>
樣式
Astro 提供多種樣式方法。最獨特的是 作用域樣式。
作用域樣式
<style> 標籤中的樣式會自動限定作用域到該元件:
<article class="card">
<h2>Title</h2>
</article>
<style>
/* Only affects .card in THIS component */
.card {
padding: 1rem;
border: 1px solid #ddd;
}
h2 {
color: navy;
}
</style>
產生的 HTML 包含唯一的類別名稱以防止樣式洩漏,因此元件樣式保持封閉,而不會升級選擇器特異性。
全域樣式
對於全站點樣式,建立一個 CSS 檔案並將其匯入佈局:
---
import "../styles/global.css";
---
條件類別
class:list 指令替代了手動類別字串構建:
Astro
---
const { featured, size = "medium" } = Astro.props;
---
<article class:list={[
"card",
size,
{ featured, "has-border": true }
]}>輸出:<article class="card medium featured has-border">
PHP
<?php
$classes = ['card', $size];
if ($featured) $classes[] = 'featured';
if (true) $classes[] = 'has-border';
?>
<article class="<?php echo esc_attr(implode(' ', $classes)); ?>"> 客戶端 JavaScript
Astro 預設傳送零 JavaScript。這是與 WordPress 最大的思維轉變。
新增互動性
對於簡單的互動,新增一個 <script> 標籤:
<button id="menu-toggle">Menu</button>
<nav id="mobile-menu" hidden>
<slot />
</nav>
<script>
const toggle = document.getElementById("menu-toggle");
const menu = document.getElementById("mobile-menu");
toggle?.addEventListener("click", () => {
menu?.toggleAttribute("hidden");
});
</script>
指令碼會自動打包和去重。如果這個元件在頁面上出現兩次,指令碼只執行一次。
進階互動元件
對於更複雜的互動性,Astro 可以按需載入 JavaScript 元件(React、Vue、Svelte)。這是可選的 — 大多數站點僅使用 <script> 標籤就可以正常工作。以下頁面僅在捲動到檢視時才載入元件:
---
import SearchWidget from "../components/SearchWidget.jsx";
---
<!-- Only load JavaScript when the search box scrolls into view -->
<SearchWidget client:visible />
| 指令 | JavaScript 何時載入 |
|---|---|
client:load | 頁面載入時立即 |
client:visible | 當元件進入檢視區時 |
client:idle | 當瀏覽器閒置時 |
路由
Astro 使用 基於檔案的路由。src/pages/ 中的檔案變成 URL:
| 檔案 | URL |
|---|---|
src/pages/index.astro | / |
src/pages/about.astro | /about |
src/pages/posts/index.astro | /posts |
src/pages/posts/[slug].astro | /posts/hello-world |
src/pages/[...slug].astro | 任意路徑(全捕獲) |
動態路由
對於 CMS 內容,對動態段使用方括號語法:
---
import { getEmDashCollection, getEmDashEntry } from "emdash";
import Base from "../../layouts/Base.astro";
import { PortableText } from "emdash/ui";
// For static builds, define which pages to generate
export async function getStaticPaths() {
const { entries: posts } = await getEmDashCollection("posts");
return posts.map(post => ({
params: { slug: post.id },
props: { post },
}));
}
const { post } = Astro.props;
---
<Base title={post.data.title}>
<article>
<h1>{post.data.title}</h1>
<PortableText value={post.data.content} />
</article>
</Base>
與 WordPress 比較
| WordPress | Astro |
|---|---|
模板層次結構(single-post.php) | 明確的檔案:posts/[slug].astro |
重寫規則 + query_vars | 檔案結構 |
$wp_query 確定模板 | URL 直接對映到檔案 |
add_rewrite_rule() | 建立檔案或資料夾 |
WordPress 概念存在的位置
查詢 WordPress 功能的 Astro/EmDash 等價物的參考:
模板
| WordPress | Astro/EmDash |
|---|---|
| 模板層次結構 | src/pages/ 中基於檔案的路由 |
get_template_part() | 匯入並使用元件 |
the_content() | <PortableText value={content} /> |
the_title()、the_*() | 透過 post.data.title 存取 |
| 模板標籤 | 模板表示式 {value} |
body_class() | class:list 指令 |
資料和查詢
| WordPress | Astro/EmDash |
|---|---|
WP_Query | getEmDashCollection(type, filters) |
get_post() | getEmDashEntry(type, id) |
get_posts() | getEmDashCollection(type) |
get_the_terms() | 透過 entry.data.categories 存取 |
get_post_meta() | 透過 entry.data.fieldName 存取 |
get_option() | getSiteSettings() |
wp_nav_menu() | getMenu(location) |
可擴充性
| WordPress | Astro/EmDash |
|---|---|
add_action() | EmDash 鉤子、Astro 中介軟體 |
add_filter() | EmDash 鉤子 |
add_shortcode() | Portable Text 自訂區塊 |
register_block_type() | Portable Text 自訂區塊 |
register_sidebar() | EmDash 小工具區域 |
| 外掛 | Astro 整合 + EmDash 外掛 |
內容型別
| WordPress | Astro/EmDash |
|---|---|
register_post_type() | 在管理 UI 中建立集合 |
register_taxonomy() | 在管理 UI 中建立分類法 |
register_meta() | 將欄位新增到集合架構 |
| 文章狀態 | 條目狀態(draft、published 等) |
| 特色圖片 | 媒體引用欄位 |
| Gutenberg 區塊 | Portable Text 區塊 |
概念對映
本指南涵蓋的主要 WordPress 到 Astro 的轉變:
- PHP 模板變成 Astro 元件:伺服器程式碼加 HTML,具有明確的檔案組織。
- 模板標籤變成 props 和匯入:資料透過引數而不是全域變數流動。
- 主題檔案變成 pages 目錄:URL 與檔案結構匹配。
- 鉤子變成插槽和中介軟體:插入點在傳遞內容的地方定義。
- jQuery 在 WordPress 中預設載入;Astro 在您新增之前不傳送 JavaScript。
從 Getting Started 指南開始構建您的第一個 EmDash 站點,或探索 Working with Content 以瞭解如何查詢和渲染 CMS 資料。