跳转到内容

移植 WordPress 主题

WordPress 主题可以系统地转换为 EmDash 主题。视觉设计、内容结构和动态功能都可以通过一个三阶段方法进行迁移。

  1. 设计提取

    从 WordPress 主题中提取 CSS 变量、字体、颜色和布局模式。分析实时网站以捕获计算后的样式和响应式断点。

  2. 模板转换

    将 PHP 模板转换为 Astro 组件。将 WordPress 模板层次结构映射到 Astro 路由,并将模板标签转换为 EmDash API 调用。

  3. 动态功能

    将导航菜单、小工具区域、分类法和站点设置移植到其对应的 EmDash 等效项。创建一个种子文件来捕获完整的内容模型。

文件用途
style.css包含主题头部信息的主样式表
assets/css/额外的样式表
theme.json区块主题 (WP 5.9+) - 结构化令牌
WordPress 模式EmDash 变量
正文字体族--font-body
标题字体--font-heading
主色调--color-primary
背景色--color-base
文本颜色--color-contrast
内容宽度--content-width

使用提取的 CSS 变量、页头/页脚结构、字体加载和响应式断点创建 src/layouts/Base.astro:

src/layouts/Base.astro
---
import { getSiteSettings, getMenu } from "emdash";
import "../styles/global.css";
const { title, description } = Astro.props;
const settings = await getSiteSettings();
const primaryMenu = await getMenu("primary");
const pageTitle = title ? `${title} | ${settings.title}` : settings.title;
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{pageTitle}</title>
</head>
<body>
<header>
{settings.logo ? (
<img src={settings.logo.url} alt={settings.title} />
) : (
<span>{settings.title}</span>
)}
<nav>
{primaryMenu?.items.map((item) => (
<a href={item.url}>{item.label}</a>
))}
</nav>
</header>
<main><slot /></main>
</body>
</html>
WordPress 模板Astro 路由
index.phpsrc/pages/index.astro
single.phpsrc/pages/posts/[slug].astro
single-{post_type}.phpsrc/pages/{type}/[slug].astro
page.phpsrc/pages/[...slug].astro
archive.phpsrc/pages/posts/index.astro
category.phpsrc/pages/categories/[slug].astro
tag.phpsrc/pages/tags/[slug].astro
search.phpsrc/pages/search.astro
404.phpsrc/pages/404.astro
header.php / footer.php作为 src/layouts/Base.astro 的一部分
sidebar.phpsrc/components/Sidebar.astro
WordPress 函数EmDash 等效项
have_posts() / the_post()getEmDashCollection()
get_post()getEmDashEntry()
the_title()post.data.title
the_content()<PortableText value={post.data.content} />
the_excerpt()post.data.excerpt
the_permalink()/posts/${post.slug}
the_post_thumbnail()post.data.featured_image
get_the_date()post.data.publishedAt
get_the_category()getEntryTerms(coll, id, "categories")
get_the_tags()getEntryTerms(coll, id, "tags")
archive.php
<?php while (have_posts()) : the_post(); ?>
<article>
<h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
<?php the_excerpt(); ?>
</article>
<?php endwhile; ?>
single.php
<?php get_header(); ?>
<article>
<h1><?php the_title(); ?></h1>
<?php the_content(); ?>
<div class="post-meta">
Posted in: <?php the_category(', '); ?>
</div>
</article>
<?php get_footer(); ?>

WordPress 的 get_template_part() 调用变为 Astro 组件导入。template-parts/content-post.php 部分模板变为一个 PostCard.astro 组件,你可以在循环中导入并渲染它。

在 functions.php 中识别菜单并创建对应的 EmDash 菜单:

src/components/PrimaryNav.astro
---
import { getMenu } from "emdash";
const menu = await getMenu("primary");
---
{menu && (
<nav class="primary-nav">
<ul>
{menu.items.map((item) => (
<li>
<a href={item.url} aria-current={Astro.url.pathname === item.url ? "page" : undefined}>
{item.label}
</a>
{item.children.length > 0 && (
<ul class="submenu">
{item.children.map((child) => (
<li><a href={child.url}>{child.label}</a></li>
))}
</ul>
)}
</li>
))}
</ul>
</nav>
)}

识别主题中的小工具区域并渲染它们:

src/components/Sidebar.astro
---
import { getWidgetArea, getMenu } from "emdash";
import { PortableText } from "emdash/ui";
import RecentPosts from "./widgets/RecentPosts.astro";
const sidebar = await getWidgetArea("sidebar");
const widgetComponents = { "core:recent-posts": RecentPosts };
---
{sidebar && sidebar.widgets.length > 0 && (
<aside class="sidebar">
{sidebar.widgets.map(async (widget) => (
<div class="widget">
{widget.title && <h3>{widget.title}</h3>}
{widget.type === "content" && <PortableText value={widget.content} />}
{widget.type === "menu" && (
<nav>
{await getMenu(widget.menuName).then((m) =>
m?.items.map((item) => <a href={item.url}>{item.label}</a>)
)}
</nav>
)}
{widget.type === "component" && widgetComponents[widget.componentId] && (
<Fragment>
{(() => {
const Component = widgetComponents[widget.componentId];
return <Component {...widget.componentProps} />;
})()}
</Fragment>
)}
</div>
))}
</aside>
)}
WordPress 小工具EmDash 小工具类型
文本/自定义 HTMLtype: "content"
自定义菜单type: "menu"
近期文章component: "core:recent-posts"
分类目录component: "core:categories"
标签云component: "core:tag-cloud"
搜索component: "core:search"

查询主题中注册的分类法:

src/pages/genres/[slug].astro
---
import { getTaxonomyTerms, getEntriesByTerm } from "emdash";
import Base from "../../layouts/Base.astro";
export async function getStaticPaths() {
const genres = await getTaxonomyTerms("genre");
return genres.map((genre) => ({
params: { slug: genre.slug },
props: { genre },
}));
}
const { genre } = Astro.props;
const books = await getEntriesByTerm("books", "genre", genre.slug);
---
<Base title={genre.label}>
<h1>{genre.label}</h1>
{books.map((book) => (
<article>
<h2><a href={`/books/${book.slug}`}>{book.data.title}</a></h2>
</article>
))}
</Base>
WordPress 定制器EmDash 设置
站点标题title
副标题tagline
站点图标favicon
自定义 Logologo
每页文章数postsPerPage

WordPress 简码变为 Portable Text 自定义区块:

functions.php
add_shortcode('gallery', function($atts) {
$ids = explode(',', $atts['ids']);
return '<div class="gallery">...</div>';
});

在种子文件中捕获完整的内容模型。包括设置、分类法、菜单和小工具区域:

.emdash/seed.json
{
"$schema": "https://emdashcms.com/seed.schema.json",
"version": "1",
"meta": { "name": "Ported Theme" },
"settings": { "title": "My Site", "tagline": "Welcome", "postsPerPage": 10 },
"taxonomies": [
{
"name": "category",
"label": "Categories",
"hierarchical": true,
"collections": ["posts"]
}
],
"menus": [
{
"name": "primary",
"label": "Primary Navigation",
"items": [
{ "type": "custom", "label": "Home", "url": "/" },
{ "type": "custom", "label": "Blog", "url": "/posts" }
]
}
],
"widgetAreas": [
{
"name": "sidebar",
"label": "Main Sidebar",
"widgets": [
{
"type": "component",
"componentId": "core:recent-posts",
"props": { "count": 5 }
}
]
}
]
}

有关完整规范,请参阅 种子文件格式。

阶段 1 (设计): CSS 变量已提取,字体加载正常,配色方案匹配,响应式断点工作正常。

阶段 2 (模板): 首页、单篇文章、归档页和 404 页面均能正确渲染。

阶段 3 (动态): 站点设置已配置,菜单功能正常,分类法可查询,小工具区域正常渲染,种子文件完整。

如果主题有父主题(检查 style.css 中的 Template:),请先分析父主题,然后应用子主题的覆盖。

WordPress 5.9+ 的区块主题使用 theme.json 存储设计令牌,使用 templates/*.html 存储区块标记。将区块标记转换为 Astro 组件,并从 theme.json 中提取令牌。

使用 Elementor、Divi 或类似工具构建的内容存储在文章元数据中,而非主题文件中。这些内容通过 WXR 导入,而非主题移植。将主题移植的重点放在外壳上——页面构建器内容在导入后通过 Portable Text 渲染。