Ein WordPress-Theme portieren

Auf dieser Seite

Portieren Sie ein WordPress-Theme, indem Sie sein Design, seine Routen-Templates und die datenbankverwalteten Funktionen voneinander trennen. Das Ergebnis ist ein vollständiges Astro-Projekt mit EmDash-Abfragen und einer Seed-Datei, nicht ein PHP-Theme, das von einer Theme-Runtime geladen wird.

Mit Inhalten und URLs beginnen

Importieren Sie repräsentative Inhalte, bevor Sie Templates umwandeln. Halten Sie die WordPress-Beitragstypen, Taxonomien, Permalink-Regeln, Menüs, Widget-Bereiche, Customizer-Werte, Shortcodes und das von Plugins erzeugte Markup fest, die die Website verwendet.

Legen Sie für jeden Inhaltstyp die neue URL fest. Astro-Routen sind explizite Dateien, daher gibt es kein automatisches Gegenstück zur WordPress-Template-Hierarchie.

Das aktuelle Blog-Template verwendet diese Zuordnung:

WordPressEmDash-Template-Pfad
front-page.php oder home.phpsrc/pages/index.astro
single.phpsrc/pages/posts/[slug].astro
archive.phpsrc/pages/posts/index.astro
page.phpsrc/pages/pages/[slug].astro
category.phpsrc/pages/category/[slug].astro
tag.phpsrc/pages/tag/[slug].astro
search.phpsrc/pages/search.astro
404.phpsrc/pages/404.astro
header.php und footer.phpsrc/layouts/Base.astro
template-parts/content.phpsrc/components/PostCard.astro oder eine andere Komponente

Das Starter-Template verwendet für Seiten stattdessen src/pages/[slug].astro. Wählen Sie ein Routenmodell und sorgen Sie dafür, dass urlPattern und Weiterleitungen des Seeds dazu passen.

Das Design extrahieren

Sammeln Sie die Quelldateien, die das gerenderte Design bestimmen:

  • style.css und eingebundene Stylesheets.
  • theme.json bei einem Block-Theme.
  • Template-Markup und Template-Teile.
  • Schriften, Icons und Bilder samt ihren Lizenzen.
  • Responsive Breakpoints und interaktives Verhalten.

Kopieren Sie die Design-Tokens in das CSS des Astro-Projekts und bauen Sie die gemeinsame Dokumenthülle in src/layouts/Base.astro. Wandeln Sie wiederkehrende Template-Teile in Komponenten um, bevor Sie einzelne Routen ausbauen.

Kopieren Sie keine von WordPress erzeugten Klassennamen oder JavaScript, wenn die neue Komponente deren Verhalten nicht braucht. Erhalten Sie das sichtbare Layout und die Interaktion, nicht die Überreste der Implementierung.

Archiv-Abfragen umwandeln

WP_Query wird zu getEmDashCollection(). Filtern und sortieren Sie in der Abfrage, damit die Datenbank die Arbeit übernimmt.

Die folgenden Beispiele rendern ein Archiv der neuesten Beiträge:

WordPress

<?php
$posts = new WP_Query([
  'post_type' => 'post',
  'post_status' => 'publish',
  'posts_per_page' => 12,
  'orderby' => 'date',
  'order' => 'DESC',
]);

while ($posts->have_posts()) :
  $posts->the_post();
?>
  <article>
    <h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
  </article>
<?php endwhile; wp_reset_postdata(); ?>

EmDash

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

const { entries: posts, error, cacheHint } = await getEmDashCollection("posts", {
  orderBy: { published_at: "desc" },
  limit: 12,
});

if (error) return new Response("Could not load posts", { status: 500 });
if (Astro.cache?.enabled) Astro.cache.set(cacheHint);
---

{posts.map((post) => (
  <article>
    <h2><a href={`/posts/${post.id}`}>{post.data.title}</a></h2>
  </article>
))}

Verwenden Sie orderBy, nicht sort oder sortBy. Die Schlüssel sind gespeicherte Feldnamen wie published_at, created_at oder ein indiziertes Sammlungsfeld.

Templates für einzelne Einträge umwandeln

Verwenden Sie den Slug aus Astro.params, um getEmDashEntry() aufzurufen. Rendern Sie Rich Text mit PortableText.

---
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, cacheHint } = await getEmDashEntry("posts", slug);
if (error) return new Response("Could not load the post", { status: 500 });
if (!post) return Astro.rewrite("/404");
if (Astro.cache?.enabled) Astro.cache.set(cacheHint);
---

<Base title={post.data.title} content={{ collection: "posts", id: post.data.id, slug }}>
  <article>
    <h1 {...post.edit.title}>{post.data.title}</h1>
    <PortableText value={post.data.content} />
  </article>
</Base>

post.id ist Astros Routenbezeichner und normalerweise der Slug. post.data.id ist die gespeicherte EmDash-Content-ID. Verwenden Sie data.id für Kommentare, Taxonomie-Helper und den Kontext der visuellen Bearbeitung.

Taxonomie-Archive umwandeln

Lösen Sie einen Term mit dem Namen seiner Taxonomie auf und verwenden Sie diesen Namen dann im where-Filter der Sammlung. Das aktuelle Blog-Template verwendet Taxonomienamen und Routenverzeichnisse im Singular:

---
import { decodeSlug, getEmDashCollection, getTerm } from "emdash";

const slug = decodeSlug(Astro.params.slug);
const term = slug ? await getTerm("category", slug, { includeCounts: false }) : null;
if (!term) return Astro.rewrite("/404");

const { entries: posts } = await getEmDashCollection("posts", {
  where: { category: term.slug },
  orderBy: { published_at: "desc" },
});
---

<h1>{term.label}</h1>
{posts.map((post) => <a href={`/posts/${post.id}`}>{post.data.title}</a>)}

Verwenden Sie getTermsForEntries() mit entry.data.id, wenn ein Archiv die Terms für viele Einträge braucht. Das bündelt die Relationsabfrage, statt in einer Schleife einen Helper pro Eintrag aufzurufen.

Dynamische Theme-Funktionen umwandeln

Menüs

Legen Sie Menüdefinitionen in seed/seed.json an und fragen Sie sie mit getMenu("primary") ab. Menüeinträge lassen sich nach dem Setup umsortieren und bearbeiten, duplizieren Sie dieselbe Navigation also nicht als fest codierte Links.

Die folgende Komponente behält verschachtelte Menüeinträge bei und markiert die aktuelle Seite:

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

const menu = await getMenu("primary");
---

{menu && (
  <nav aria-label="Primary navigation">
    <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>
              {item.children.map((child) => (
                <li><a href={child.url}>{child.label}</a></li>
              ))}
            </ul>
          )}
        </li>
      ))}
    </ul>
  </nav>
)}

Widget-Bereiche

Definieren Sie Widget-Bereiche im Seed und rendern Sie sie mit <WidgetArea name="sidebar" /> aus emdash/ui. Verwenden Sie Komponenten-Widgets nur, wenn das Template die passende Komponenten-ID registriert.

---
import { WidgetArea } from "emdash/ui";
---

<main><slot /></main>
<aside aria-label="Related content">
  <WidgetArea name="sidebar" />
</aside>

Website-Identität

Lesen Sie Website-Titel, Untertitel, Logo und Favicon mit getSiteSettings(). Statische rechtliche oder gestalterische Texte können im Template bleiben, die von Administratoren verwaltete Identität muss jedoch aus den Einstellungen kommen.

WordPress-WertEmDash-Einstellung
Website-Titeltitle
Untertiteltagline
Eigenes Logologo
Website-Iconfavicon
Beiträge pro SeitepostsPerPage

Seiten-Templates

Wenn Editoren zwischen mehreren Seitenlayouts wählen müssen, fügen Sie der Seiten-Sammlung ein Select-Feld hinzu und ordnen Sie jeden gespeicherten Wert einer bekannten Komponente zu. Machen Sie aus einem gespeicherten String keinen beliebigen Importpfad.

Shortcodes und Blöcke

Ordnen Sie inhaltstragende Shortcodes und WordPress-Blöcke Portable-Text-Strukturen zu. Verwenden Sie für eingebaute Blöcke den gewöhnlichen PortableText-Renderer und für benutzerdefinierte _type-Werte eine Komponenten-Map.

Ein Seed kann benutzerdefinierte Blockdaten liefern, registriert aber keinen benutzerdefinierten Editor. Verwenden Sie ein natives Plugin, wenn die Portierung paketierte Astro-Renderer oder eine eigene React-Bearbeitungsoberfläche benötigt.

Ordnen Sie beispielsweise einen WordPress-Galerie-Shortcode einem _type mit Namespace zu, etwa publication.gallery, erstellen Sie einen Gallery.astro-Renderer und übergeben Sie ihn über die Komponenten-Map von PortableText:

---
import type { PortableTextBlock } from "emdash";
import { PortableText } from "emdash/ui";
import Gallery from "./blocks/Gallery.astro";

interface Props {
  value: PortableTextBlock[];
}

const { value } = Astro.props;
const customTypes = { "publication.gallery": Gallery };
---

<PortableText value={value} components={{ type: customTypes }} />

Den Seed erstellen

In aktuellen Templates liegt der Seed unter seed/seed.json, und package.json#emdash.seed verweist darauf. Deklarieren Sie jedes Sammlungsfeld, jede Taxonomie, jedes Menü, jeden Widget-Bereich, jede Section und jeden Beispieleintrag, die das Template voraussetzt.

Das folgende Fragment definiert die routenrelevanten Felder für Beiträge:

{
  "$schema": "https://emdashcms.com/seed.schema.json",
  "version": "1",
  "collections": [
    {
      "slug": "posts",
      "label": "Posts",
      "labelSingular": "Post",
      "supports": ["drafts", "revisions", "search", "seo"],
      "urlPattern": "/posts/{slug}",
      "fields": [
        { "slug": "title", "label": "Title", "type": "string", "required": true },
        { "slug": "content", "label": "Content", "type": "portableText" },
        { "slug": "excerpt", "label": "Excerpt", "type": "text" },
        { "slug": "featured_image", "label": "Featured image", "type": "image" }
      ]
    }
  ]
}

Lesen Sie Seed-Dateien für Content-IDs, Referenzen, Lokalisierung, Medien, Validierung und Apply-Optionen.

In überprüfbaren Etappen portieren

  1. Importieren Sie einen repräsentativen WordPress-Export und halten Sie nicht unterstützte Blöcke oder Felder fest.

  2. Beginnen Sie mit dem aktuellen EmDash-Template für das Deployment-Ziel.

  3. Implementieren Sie das gemeinsame Layout, die Tokens, die Typografie und die responsive Hülle.

  4. Implementieren Sie Archiv- und Einzeleintrags-Routen mit den importierten Sammlungs-Slugs und Feldern.

  5. Fügen Sie Taxonomie-Routen, Suche, Menüs, Widget-Bereiche und Website-Einstellungen hinzu, die das Quell-Theme tatsächlich verwendet.

  6. Fügen Sie Seed-Definitionen für jede Routenabhängigkeit hinzu und testen Sie das Setup gegen eine leere Datenbank.

  7. Vergleichen Sie repräsentative öffentliche URLs in mobiler und Desktop-Breite. Testen Sie leere Felder, lange Titel, fehlende Bilder, Entwürfe und 404-Routen.

  8. Bereiten Sie vor dem Umschalten Weiterleitungen für jeden geänderten Permalink vor.

Schwierige WordPress-Themes behandeln

  • Child-Themes: Kombinieren Sie die aufgelöste Ausgabe von Parent- und Child-Theme. Portieren Sie die tatsächlich wirksamen Templates und Styles, nicht nur das Child-Verzeichnis.
  • Block-Themes: Verwenden Sie theme.json als Quelle für Design-Tokens und templates/*.html als Inhaltsstruktur und drücken Sie das Ergebnis dann als Astro-Komponenten aus.
  • Page Builder: Importieren Sie zuerst repräsentative Seiten. Shortcodes und proprietäres JSON von Buildern erfordern meist eine bewusste Umwandlung in Portable Text oder ein neu gestaltetes Seiten-Template.
  • WooCommerce-Themes: Behandeln Sie das Commerce-Verhalten als separate Anwendungsintegration. Das Portieren von Darstellungsdateien ersetzt nicht die Semantik von Produkt, Warenkorb, Checkout, Zahlung oder Bestellung.