Erstellung einer Gatsby-Website mit WordPress Daten

Avatar of Ganesh Dahal
Ganesh Dahal am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

In meinem vorherigen Artikel letzte Woche erwähnte ich die Erstellung einer teilweise portierten WordPress-Gatsby-Website. Dieser Artikel ist eine Fortsetzung mit einer Schritt-für-Schritt-Anleitung unter der Haube.

Gatsby, ein auf React basierendes Framework für statische Websites, erregt nicht nur die Aufmerksamkeit von JavaScript-Entwicklern, sondern auch von WordPress-Entwicklern und -Nutzern. Viele WordPress-Nutzer finden seine Funktionen ansprechend, wie die ultraschnelle Bildhandhabung und verbesserte Sicherheitsvorkehrungen gegen Hacker, möchten diese aber weiterhin nutzen, während sie weiterhin das WordPress-Admin-Panel und den Editor zur Verwaltung von Inhalten verwenden.

Chris hat die Idee Gatsby und WordPress zu kombinieren bereits hier auf CSS-Tricks behandelt. Als WordPress-Enthusiast beschloss ich, es auszuprobieren. Dieser Artikel basiert auf dem, was ich auf dem Weg gelernt und dokumentiert habe.

Bitte beachten Sie, dass WPGraphQL und gatsby-cli sich in ständiger Entwicklung befinden und spätere Versionen unter Umständen nicht abwärtskompatibel sind. Dieses Projekt wurde mit WPGraphQL 0.8.3, gatsby-source-wpgraphql 2.5.1 und gatsby-cli 2.12.21 durchgeführt. Im Gegensatz zu WordPress unterstützen neuere WPGraphQL-Versionen keine Abwärtskompatibilität. Bitte konsultieren Sie die offiziellen WPGraphQL- &-Gatsby-Dokumentation für die neuesten Änderungen und gehen Sie mit Vorsicht vor, bevor Sie sie verwenden.

Es gibt einsatzbereite Projekte in der Gatsby-Starter-Bibliothek. Zwei großartige Beispiele sind Alexandra Spalatoss gatsby-wordpress-theme-blog und das twenty-nineteen-gatsby-theme von Zac Gordon und Muhammad Muhsin.

Voraussetzungen

Wenn Sie mitmachen möchten, benötigen Sie Folgendes

Assets und Ressourcen

Da ich bereits einige Gatsby-Lernprojekte in der Vergangenheit durchgeführt hatte, hatte ich einige Assets wie Typografie, Layouts und andere wiederverwendbare Komponenten, die ich hier anwenden konnte. Ich hatte auch die folgenden aktuellen Tutorial-Anleitungen durchgearbeitet, die mir bei der Vorbereitung für dieses Projekt geholfen haben.

Henrik Wirths Anleitung ist super umfassend und detailliert. Jasons Schritt-für-Schritt-Artikel ist eine großartige Ressource und enthält sogar sehr hilfreiche Videos, die den Prozess veranschaulichen. Muhammads Artikel hilft zu erklären, wie statische Seiten mit Gatsbys createPages API erstellt werden und zerlegt dabei verschiedene Funktionen, Vorlagendateien und React-Komponenten.

Ich habe größtenteils Henriks Anleitung befolgt und diesen Artikel in ähnliche Abschnitte unterteilt. Henriks Anleitung enthält Bildhandhabung und das Hinzufügen von PageBuilder mit ACF Flexible Content-Funktionen, die wir hier nicht behandeln.

Abschnitt 1: Einrichtung von WordPress und Gatsby

Zuerst richten wir eine WordPress-Website als Datenquelle ein. Dies kann eine bereits bestehende Website oder eine neue sein. Eine lokale WordPress-Installation ist ebenfalls in Ordnung. Ich entschied mich, für dieses Projekt mit einer neuen Test-WordPress-Website zu beginnen, die das Twenty Twenty-Theme verwendet, das mit WordPress geliefert wird.

Installieren Sie die Plugins WPGraphQL und WPGraphiQL

Lassen Sie uns zunächst ein paar Plugins in WordPress installieren. Wir verwenden WPGraphQL, um eine GraphQL-API in WordPress zu aktivieren und WordPress als Datenquelle zu öffnen. Wir verwenden auch WPGraphiQL (beachten Sie das „i“ im Namen). Dieses ist optional, aber es erstellt eine Schnittstelle zum Testen von GraphQL-Abfragen direkt im WordPress-Dashboard, was sehr praktisch ist. Möglicherweise stellen Sie fest, dass ich auf die GitHub-Repositorys für die Plugins verlinke und nicht auf das WordPress-Plugin-Verzeichnis, und das ist beabsichtigt – keines der Plugins ist zum Zeitpunkt des Schreibens in diesem Verzeichnis verfügbar. Daher laden Sie die ZIP-Dateien herunter und installieren sie manuell in WordPress über das Verzeichnis /wp-content/plugins.

Nach der Aktivierung wird die GraphiQL-API im WordPress-Dashboard angezeigt.

Die GraphiQL-API bietet eine Spielwiese zum Testen von GraphQL-Abfragen von der WordPress-Website aus.

Screenshot of the GraphQL playground interface in the WordPress dashboard.
Der GraphiQL-Bildschirm bietet drei Panels: eines zum Navigieren zwischen verschiedenen Objekten (links), eines zum Abfragen von Daten (mitte) und eines zur Visualisierung der zurückgegebenen Daten (rechts).

Einrichtung einer lokalen Gatsby-Website

Wir richten eine lokale Gatsby-Website ein, indem wir Gatsby's starter default im Verzeichnis wordpress-gatsby des Projekts über die Befehlszeile installieren

#! create a new Gatsby site using the default starter
gatsby new wordpress-gatsby https://github.com/gatsbyjs/gatsby-starter-default

Starten Sie den Server mit gatsby develop neu, navigieren Sie dann zu localhost:8000 in einem neuen Browser-Tab. Wir sollten eine Starter-Seite im Browser erhalten.

Ein Link zu Anleitung zur Erstellung einer Gatsby-Website lokal ist in der Gatsby-Dokumentation verfügbar.

Als Nächstes installieren und konfigurieren wir das Plugin gatsby-source-graphql. Genau wie bei der Einrichtung von WordPress müssen wir WPGraphQL in der Gatsby-Website installieren und konfigurieren.

#! install wpgraphql plugin
#! add with yarn
yarn add gatsby-source-graphql
#! install with npm
npm install --save gatsby-source-graphql

OK, jetzt ist es an der Zeit, das Plugin gatsby-source-graphql zu konfigurieren. Öffnen Sie die Datei gatsby-config.js und verwenden wir diese Einstellungen

// plugin configuration
module.exports = {
  plugins: [
    {
      resolve: "gatsby-source-graphql",
      options: {
        typeName: "WPGraphQL",
        fieldName: "wpcontent",
        // GraphQL endpoint, relative to your WordPress home URL.
        url: "https://tinjurewp.com/wp-gatsby/graphql",
        // GraphQL endpoint using env variable
       // url: "${process.env.WORDPRESS_URL}/graphql",
      },
    },
  ],
}

Wie bin ich auf diese genaue Konfiguration gekommen? Ich habe mich streng an die Angaben in den Gatsby-Dokumenten gehalten. Das Plugin wurde zur Gatsby-Instanz hinzugefügt, indem die URL des GraphQL-Endpunkts (oben hervorgehoben) und zwei Konfigurationsoptionen angegeben wurden: typeName, ein Typ für Remote-Schema-Abfragen, und fieldName, der in der Gatsby-Abfrage verfügbar ist. Bitte beachten Sie, dass die neueste WPGraphQL-Dokumentation vorschlägt, fieldName: "wpcontent" anstelle von "wpgraphql" zu verwenden, wie in der Anleitung beschrieben.

Alternative Einrichtung: Verwenden des dotenv-Moduls

Optional könnten wir die Dinge auch mithilfe des dotenv npm-Moduls einrichten, um Umgebungsvariablen zu definieren, die zur Anpassung der Entwicklungsumgebung verwendet werden. Henrik verwendet diese Methode auch in seiner Anleitung.

Wenn Sie diese Methode verwenden, kann eine Variable in der .env.production-Plugin-Konfigurationsdatei, wie z. B. WORDPRESS_URL, definiert und anstelle der Offenlegung der WordPress-URL verwendet werden.

# .env.production
# Don't put any sensible data here!!!
WORDPRESS_URL=https://tinjurewp.com/wp-gatsby/

Meine Testumgebung macht die WordPress-Instanz und -Daten gleichermaßen für WPGraphQL zugänglich.

Colby Fayock hat eine hilfreiche Schritt-für-Schritt-Anleitung zur Verwendung von Umgebungsvariablen mit Gatsby und Netlify.

Nachdem der Entwicklungsserver neu gestartet wurde, ist die WPGraphQL-API mit Gatsby verfügbar, um die spezifischen Daten abzurufen, die von der WordPress-Website abgefragt werden, und sie über die lokale GraphQL-URL unter https//:8000/___graphql/ auf einer Gatsby-Website anzuzeigen.

Screenshot showing the GraphQL query interface with the explorer on the left, the query in the center, and the returned data on the right.
Beachten Sie, dass die Daten hier, im Gegensatz zur WordPress-Website selbst, für WPGraphQL zugänglich gemacht werden. Wir können gegen die WPGraphQL-API abfragen, um jedes Feld von der WordPress-Website anzuzeigen.

Abschnitt 2: Portierung von Beiträgen und Seiten aus WordPress

In Gatsby können Beiträge und Seiten zur Build-Zeit erstellt werden, indem Daten mit GraphQL abgefragt und die Abfrageergebnisse auf Beitrags- oder Seitenvorlagen abgebildet werden. Der Prozess ist in einem Gatsby-Tutorial zur programmatischen Seitenerstellung aus Daten beschrieben. Gatsby nutzt zwei APIs, onCreateNode und createPages, und das Tutorial enthält eine detaillierte Erklärung, wie diese implementiert werden.

Die Code-Snippets hier stammen aus Henrik's Anleitung. Aufgrund der Art und Weise, wie WordPress Daten in seiner Datenbank unter verschiedenen Datentypen und Kategorien speichert, ist die Portierung aller Inhalte weniger einfach als gedacht. Mit Vorkenntnissen über die Erstellung von Seiten und Beiträgen mit Gatsbys createPages-API und Node API konnte ich jedoch mitverfolgen. Es gibt auch viele reale Starter-Websites, die als Beispiele herangezogen werden können.

Schritt 1: Hinzufügen von Inhalten für Beiträge und Seiten in einer WordPress-Website

Fügen Sie einige Beiträge und Seiten in der WordPress-Website hinzu, wenn Sie noch keine haben. Bevor Sie eine Seite für diesen Inhalt erstellen, müssen wir index.js und page-2.js aus dem Seitenordner der Gatsby-Website löschen. Diese beiden Dateien scheinen mit den portierten WordPress-Daten zu interferieren.

Schritt 2: Erstellen einer Seiten- und Beitragsvorlage

Wir erstellen zwei Vorlagendateien für unsere Inhalte, eine für Beiträge (/src/templates/posts/index.js) und eine für Seiten (/src/templates/pages/index.js).

Hier ist unsere Beitragsvorlage. Im Grunde verwenden wir den Beitragstitel zweimal (einmal als SEO-Seitentitel und einmal als Überschrift des Beitrags) und den Beitragstext alsPost-Komponente.

// src/templates/post/index.js
import React  from "react"
import Layout from "../../components/layout"
import SEO from "../../components/SEO"


const Post = ({ pageContext }) => {
  const post = pageContext.post


  return (
    <Layout>
      <SEO title={post.title} />


      <h1> {post.title} </h1>
      <div dangerouslySetInnerHTML={{__html: post.content}} />


    </Layout>
  )
}


export default Post

Für die Seitenvorlage machen wir fast dasselbe

//src/templates/pages/index.js

import React  from "react"
import Layout from "../../components/layout"
import SEO from "../../components/seo"


const Page = ({ pageContext }) => {
  const page = pageContext.page


  return (
    <Layout>
      <SEO title={page.title} />


      <h1>{page.title}</h1>
      <div dangerouslySetInnerHTML={{__html: page.content}} />


    </Layout>
  )
}


export default Page

Schritt 3: Statische Beiträge und Seiten mit der createPages-API erstellen

Beachten Sie, dass der gesamte hier behandelte Code in der Datei node.js geschrieben werden kann. Aus Gründen der Lesbarkeit sind Beiträge und Seiten jedoch in einem Ordner namens create im Stammverzeichnis des Projekts getrennt, wie in Henrik's Guide beschrieben.

Wir werden uns mit der GraphQL createPages-API beschäftigen! Wir beginnen damit, Folgendes zu gatsby-node.js hinzuzufügen.

// gatsby-node.js
const createPages = require("./create/createPages")
const createPosts = require("./create/createPosts")


 exports.createPagesStatefully = async ({ graphql, actions, reporter }, options) => {
  await createPages({ actions, graphql, reporter }, options)
  await createPosts({ actions, graphql, reporter }, options)
 }

Muhammads Beitrag macht einen guten Punkt, der hier erwähnenswert ist: 

Die createPages API ist Teil der Node APIs, die Gatsby bereitstellt. Sie weist Gatsby im Wesentlichen an, Seiten hinzuzufügen. Innerhalb dessen rufen wir mit async/await (einer Funktion von ECMAScript 2017) einige Methoden auf.

Anders ausgedrückt: Beide Funktionen erstellen relevante statische Seiten. Vor diesem Hintergrund legen wir fest, welche Daten wir verwenden möchten, und rufen diese Daten in der Datei create/createPages.js ab. Entschuldigung für die lange Code-Einleitung, aber Henriks Kommentare helfen zu erklären, was passiert.

//create/createPages.js
const pageTemplate = require.resolve('../src/templates/page/index.js');


const GET_PAGES = `
  query GET_PAGES($first:Int $after:String) {
    wpgraphql {
      pages(
        first: $first
        after: $after
        # This will make sure to only get the parent nodes and no children
        where: {
          parent: null
         }
      ) {
        pageInfo {
          hasNextPage
          endCursor
        }
        nodes {
          id
          title
          pageId
          content
          uri
          isFrontPage
        }
      }
    }
  }
`


const allPages = []
let pageNumber = 0
const itemsPerPage = 10


/** This is the export which Gatbsy will use to process.
 * @param { actions, graphql }
 * @returns {Promise<void>} */
module.exports = async ({ actions, graphql, reporter }, options) => {


  /** This is the method from Gatsby that we're going
   * to use to create pages in our static site. */
  const { createPage } = actions
  /** Fetch pages method. This accepts variables to alter
   * the query. The variable `first` controls how many items to
   * request per fetch and the `after` controls where to start in
   * the dataset.
   * @param variables
   * @returns {Promise<*>} */
  const fetchPages = async (variables) =>
    /** Fetch pages using the GET_PAGES query and the variables passed in. */
    await graphql(GET_PAGES, variables).then(({ data }) => {
      /** Extract the data from the GraphQL query results */
      const {
        wpgraphql: {
          pages: {
            nodes,
            pageInfo: { hasNextPage, endCursor },
          },
        },
      } = data


      /** Map over the pages for later creation */
      nodes
      && nodes.map((pages) => {
        allPages.push(pages)
      })


      /** If there's another page, fetch more
       * so we can have all the data we need. */
      if (hasNextPage) {
        pageNumber++
        reporter.info(`fetch page ${pageNumber} of pages...`)
        return fetchPages({ first: itemsPerPage, after: endCursor })
      }


      /** Once we're done, return all the pages
       * so we can create the necessary pages with
       * all the data on hand. */
      return allPages
    })


  /** Kick off our `fetchPages` method which will get us all
   * the pages we need to create individual pages. */
  await fetchPages({ first: itemsPerPage, after: null }).then((wpPages) => {


    wpPages && wpPages.map((page) => {
      let pagePath = `${page.uri}`


      /** If the page is the front page, the page path should not be the uri,
       * but the root path '/'. */
      if(page.isFrontPage) {
        pagePath = '/'
      }


      createPage({
        path: pagePath,
        component: pageTemplate,
        context: {
          page: page,
        },
      })


      reporter.info(`page created: ${page.uri}`)
    })


    reporter.info(`# -----> PAGES TOTAL: ${wpPages.length}`)
  })
}

Auch hier ist Muhammads Beitrag eine ausgezeichnete Hilfe, da er aufschlüsselt, was die Funktionen createPages.js und createPosts.js leisten können. Henrik's Anleitung bietet ebenfalls hilfreiche Kommentare für jeden Schritt. 

Schritt 4: Erstellen von Beiträgen

Die Datei createPosts.js ist fast identisch mit createPages.js. Der einzige Unterschied besteht darin, den Pfad mit blog/ zu präfixen und im gesamten Code „page“ durch „posts“ zu ersetzen.

Wenn wir hier anhalten und den Entwicklungsserver mit gatsby develop im Terminal neu starten, zeigt das Entwicklungsprotokoll den Seitenaufbau an.

Wenn wir nun localhost:8000 in einem Browser öffnen, erhalten wir einen 404-Fehler. 

Das mag entmutigend sein, aber es ist alles gut. Wenn Sie auf einen der Links auf der 404-Seite klicken, wird die richtige Seite oder der richtige Beitrag aus der WordPress-Datenquelle angezeigt. Wenn Sie beispielsweise auf den Link „sample-page“ klicken, wird der Inhalt der Beispielseite aus WordPress im Browser angezeigt.


Abschnitt 3: Arbeiten mit der Navigation

Kommen wir nun zum Navigationsmenü für unsere Website. WordPress verfügt über eine Navigationsverwaltungsfunktion, mit der wir Menüs mithilfe von Links zu Seiten, Beiträgen, Archiven, Taxonomien und sogar benutzerdefinierten Links erstellen können. Wir möchten eine Navigation für ein Hauptmenü in WordPress erstellen und diese an GraphQL senden, wo wir sie für unsere eigene Website abfragen können.

Navigationslinks – einschließlich Seiten- und Beitragslinks – werden in Gatsby mithilfe der Gatsby Link API erstellt, die sowohl die integrierte <Link>-Komponente als auch die navigate-Funktion verwendet. Die <Link>-Komponente wird zum Verknüpfen interner Seiten verwendet, nicht jedoch externer Links.

Die Portierung des Navigationsmenüs von WordPress in die Gatsby-Website erweist sich als knifflige Aufgabe, die die Erstellung von <Menu>- und <MenuItems>-Komponenten sowie die entsprechende Überarbeitung der <Layout>-Komponente erfordert. Hier erfahren Sie, wie das funktioniert.

Die in diesem Abschnitt verwendeten Code-Snippets stammen direkt aus Henrik's Anleitung zur Vollständigkeit. Diese Code-Snippets scheinen jedoch in anderen Gatsby WordPress-Starter-Versionen mit geringfügigen Abweichungen ziemlich standardmäßig verwendet zu werden.

Schritt 1: Erstellen eines WordPress-Menüs

Wie in der Anleitung beschrieben, ist es wichtig, ein Menü namens „PRIMARY“ einzurichten, das im Twenty Twenty-Theme definiert ist. Wir werden drei Links hinzufügen

  • Startseite: Ein Link zu unserer Startseite, der ein benutzerdefinierter Link sein wird, der auf den Index unserer Website verweist.
  • Beispielseite: Die Standardseite, die WordPress bei einer neuen WordPress-Installation erstellt.
  • Startseite: Dies ist normalerweise der Name, der der Homepage in WordPress zugewiesen wird. Sie müssen diese Seite im Editor erstellen.

Schritt 2: Menüelemente mit dem GraphiQL Explorer abfragen

Als Nächstes schreiben wir eine Abfrage für die Menüelemente über die GraphiQL-Oberfläche. Beachten Sie, dass wir den Explorer verwenden können, um sie praktisch für uns zu schreiben, indem wir ein paar Kästchen ankreuzen.

query MyQuery {
  menuItems(where: {location: PRIMARY}) {
    nodes {
      label
      url
      title
      target
    }
  }
}

Sehen Sie, wie die URLs in den Daten absolut sind und die vollständige Adresse anzeigen? Wir benötigen eine Hilfsfunktion, um diese in relative URLs zu übersetzen, wiederum weil das die <Link>-Komponente unterstützt.

Henrik's Anleitung stellt die folgende Hilfsfunktion zur Verfügung, um absolute WordPress-URLs in relative URLs umzuwandeln, die für Gatsby erforderlich sind.

// src/utils/index.js
/** Parses a menu item object and returns Gatsby-field URI.
 * @param {object} menuItem a single menu item
 * @param wordPressUrl
 * @param blogURI */
export const CreateLocalLink = (menuItem, wordPressUrl, blogURI='blog/') => {
  const { url, connectedObject } = menuItem;


  if (url === '#') {
    return null;
  }
  /** Always want to pull of our API URL */
  let newUri = url.replace(wordPressUrl, '');


  /** If it's a blog link, respect the users blogURI setting */
  if (connectedObject && connectedObject.__typename === 'WPGraphQL_Post') {
    newUri = blogURI + newUri;
  }


  return newUri;
};

Schritt 4: Erstellen einer Menüelement-Komponente

Der nächste Schritt ist die Erstellung einer <MenuItem>-Komponente, die die im vorherigen Schritt erstellte Hilfsfunktion verwendet. Das Ergebnis ist ein vollständiger Link, der vom Menü der Gatsby-Website konsumiert wird.

// src/components/MenuItem.js
import React from "react"
import { CreateLocalLink } from "../utils"
import { Link } from "gatsby"


const MenuItem = ({ menuItem, wordPressUrl }) => {
  return (
    <Link style={{marginRight: '20px' }}
     to={CreateLocalLink(menuItem, wordPressUrl)}>
     {menuItem.label}
     </Link>
  )
}


export default MenuItem

Schritt 5: Erstellen einer Menükomponente

OK, wir haben URLs und eine funktionale <MenuItem>-Komponente erstellt. Lassen Sie uns eine neue <Menu>-Komponente erstellen, in der unsere <MenuItem>-Komponenten untergebracht werden können. Die Gatsby StaticQuery API wird verwendet, um alle primären Menüelemente mit GraphQL abzufragen.

// src/components/Menu.js
import React from "react"
import { StaticQuery, graphql } from "gatsby"
import MenuItem from "./MenuItem"


/** Define MenuItem fragment and get all primary menu items */
const MENU_QUERY = graphql`
  fragment MenuItem on WPGraphQL_MenuItem {
    id
    label
    url
    title
    target
  }


  query GETMAINMENU {
    wpgraphql {
      menuItems(where: {location: PRIMARY}) {
        nodes {
          ...MenuItem
        }
      }
      generalSettings {
        url
      }
    }
  }
`


const Menu = () => {
  return (
    <StaticQuery
      query={MENU_QUERY}
      render={(data) => {
        if (data.wpgraphql.menuItems) {
          const menuItems = data.wpgraphql.menuItems.nodes
          const wordPressUrl = data.wpgraphql.generalSettings.url


       return (
         <div style={{ marginBottom: "20px" }}>
           {
             menuItems &&
             menuItems.map((menuItem) => (
               <MenuItem key={menuItem.id}
               menuItem={menuItem} wordPressUrl={wordPressUrl}/>
             )
           )}
         </div>
       )
      }
      return null
   }}
  />
  )
}


export default Menu

Schritt 6: Hinzufügen des Menüs zur Layout-Komponente

An diesem Punkt haben wir alles Notwendige, um ein Gatsby-Site-Menü mit WordPress-Daten zu erstellen. Wir müssen nur noch die <Menu>-Komponente in unsere <Layout>-Komponente einfügen

// src/components/layout.js
import React from "react"
import PropTypes from "prop-types"
import useSiteMetadata from '../components/siteMetadata';
import Header from "./Header"
import Footer from "./Footer"
import Menu from "./Menu"
import "./layout.css"


const Layout = ({ children }) => {
  const { title, description } = useSiteMetadata();


  return (
    <section>
      <Header siteTitle={title} description={description} />
      <div
      style={{ margin: `0 auto`, maxWidth: 960,
               padding: `0 1.0875rem 1.45rem`,}}>
        <Menu />
        <main>{children}</main>
        <Footer />
      </div>
    </section>
  )
}


Layout.propTypes = {
  children: PropTypes.node.isRequired,
}


export default Layout

Die Dokumentation von Gatsby zur <Link>-Komponente erklärt, dass Daten von einem externen CMS wie WordPress idealerweise von der <Link>-Komponente inspiziert werden und entweder mit Gatsbys <Link> oder mit einem normalen <a>-Tag entsprechend gerendert werden sollten. Dies stellt sicher, dass wirklich externe Links auf der WordPress-Seite absolut bleiben, ohne mit der <Link>-Komponente zu kollidieren.

Dies erfordert – Sie haben es erraten – eine weitere Komponente, die genau das tut. In der Gatsby-Dokumentation wird sie als <UniversalLink> bezeichnet, die entweder eine Gatsby-kompatible <Link>-Komponente oder ein traditionelles <a>-Element zurückgibt.

//src/components/UniversalLink.js
import React from "react"
import { Link as GatsbyLink } from "gatsby"


const UniversalLink = ({ children, to, activeClassName, partiallyActive, ...other }) => {
  const internal = /^\/(?!\/)/.test(to)
  // Use Gatsby Link for internal links, and <a> for others
  if (internal) {
    return (
      <GatsbyLink
        to={to}
        activeClassName={activeClassName}
        partiallyActive={partiallyActive}
        {...other}
      >
        {children}
      </GatsbyLink>
    )
  }
  return (
    <a href={to} {...other}>
      {children}
    </a>
  )
}
export default UniversalLink

Gehen wir nun zurück zu unserer <MenuItem>-Komponente und aktualisieren sie, um die <UniversalLink> zu verwenden.

/ src/components/MenuItem.js
import React from "react"
import { CreateLocalLink } from "../utils"
import UniversalLink from "./UniversalLink"


const MenuItem = ({ menuItem, wordPressUrl }) => {
  return (
    <UniversalLink style={{marginRight: '20px' }}
      to={CreateLocalLink(menuItem, wordPressUrl)}>
      {menuItem.label}
    </UniversalLink>
  )
}


export default MenuItem

Sind Sie bereit, sich das anzusehen? Starten Sie den lokalen Server mit gatsby develop neu, und der Browser sollte ein Navigationsmenü mit Elementen anzeigen, die Links zu relativen Seitenpfaden enthalten.

Screenshot showing the Same Page title and content under the site navigation, which includes the site title, and menu items.
Erstellt in WordPress, angezeigt in Gatsby.

Abschnitt 4: Anzeige von Blogbeiträgen in Gatsby

Wir sind zu diesem Zeitpunkt ziemlich gut aufgestellt, aber es gibt ein großes Stück, das wir angehen müssen: die Anzeige von Seiten auf der Website. In diesem Abschnitt werden wir die Schritte durchgehen, um dies zu erreichen, insbesondere durch die Erstellung von Blogbeitragsvorlagen sowie einiger neuer Komponenten für Beitragsbilder, bevor alles in createPages.js und createPosts.js zusammengeführt wird.

Haben Sie Ihre Seiten und Beiträge bereits in WordPress erstellt? Wenn nicht, ist jetzt ein guter Zeitpunkt, dorthin zu springen und es zu tun.

Schritt 1: Hinzufügen einer globalen Variablen-Datei im Stammverzeichnis des Projekts

// global variable
const Globals = {
  blogURI: ''
}
module.exports = Globals

Der URL-Pfad blogURI = ' ' wird verwendet, wenn die Einstellung für die Startseite im WordPress-Admin (EinstellungenLesen) auf „Ihre neuesten Beiträge“ gesetzt ist.

Wenn Sie stattdessen die Option „Statische Seite“ verwenden möchten, sollte blogURI= 'blog' in der globalen Variablen-Datei verwendet werden.

Schritt 2: Erstellen einer Blog-Vorlage im Templates-Ordner

Diese Vorlage wird für die Anzeige aller veröffentlichten Beiträge verwendet. Beachten Sie, dass hier zwei Komponenten – PostEntry und Pagination (die beide noch nicht existieren) – verwendet werden. Wir werden uns diesen gleich zuwenden.

// src/templates/post/blog.js
import React from "react"
import Layout from "../../components/Layout"
import PostEntry from "../../components/PostEntry"
import Pagination from "../../components/Pagination"
import SEO from "../../components/SEO"


const Blog = ({ pageContext }) => {
  const { nodes, pageNumber, hasNextPage, itemsPerPage, allPosts }
  = pageContext


  return (
    <Layout>
      <SEO
        title="Blog"
        description="Blog posts"
        keywords={[`blog`]}
      />
      {nodes && nodes.map(post => <PostEntry key={post.postId}
        post={post}/>)}
      <Pagination
        pageNumber={pageNumber}
        hasNextPage={hasNextPage}
        allPosts={allPosts}
        itemsPerPage={itemsPerPage}
      />
    </Layout>
  )
}


export default Blog

Schritt 3. Erstellen einer Beitrags-Entry-Komponente

Diese Komponente wird in archive.js und anderen Komponenten verwendet, um Beiträge zu durchlaufen und den Titel des Beitrags-Entries, das Featured Image (falls vorhanden), den Auszug und die URL (auch als „Slug“ in WordPress-Terminologie bekannt) anzuzeigen.

// src/components/PostEntry.js
import React from "react"
import { Link } from "gatsby"
import Image from "./Image"
import { blogURI } from "../../globals"


const PostEntry = ({ post }) => {
  const { uri, title, featuredImage, excerpt } = post

  return (
    <div style={{ marginBottom: "30px" }}>
      <header>
        <Link to={`${blogURI}/${uri}/`}>
          <h2 style={{ marginBottom: "5px" }}>{title}</h2>
          <Image image={featuredImage} style={{ margin: 0 }}/>
        </Link>
      </header>


      <div dangerouslySetInnerHTML={{ __html: excerpt }}/>
    </div>
  )
}

export default PostEntry

Schritt 4: Erstellen einer (optionalen) Bildkomponente

Der Standard-Starter von Gatsby kommt mit einer Image-Komponente, die in den meisten Fällen gut funktioniert. In diesem Beispiel rufen wir die Bilddatei ab, die als Featured Image des Beitrags in WordPress verwendet wird, und weisen ihr ein Fallback-Bild zu, falls kein Featured Image vorhanden ist, wie in Henrik's Anleitung beschrieben.

// src/components/Image.js
import React from "react"
import { useStaticQuery, graphql } from "gatsby"


const Image = ({ image, withFallback = false, ...props }) => {
  const data = useStaticQuery(graphql`
    query {
      fallBackImage: file(relativePath: { eq: "fallback.svg" }) {
        publicURL
      }
    }
  `)


  /* Fallback image */
  if (!image) {
    return withFallback ? <img src={data.fallBackImage.publicURL}
      alt={"Fallback"} {...props}/> : null
  }


  return <img src={image.sourceUrl} alt={image.altText} {...props}/>
}


export default Image

Wenn withFallback auf false gesetzt ist (wie in der Standard-Gatsby-Komponentendatei), wird einfach kein DOM-Element gerendert.

Schritt 5: Erstellen einer Paginierungs-Komponente

Die Paginierungs-Komponente ermöglicht es uns, eine bestimmte Anzahl von Beiträgen pro Seite im Beitragsverzeichnis anzuzeigen. WordPress hat zwei Arten von Paginierung: eine, die „Weiter“ und „Zurück“-Links zurückgibt, um seitenweise zu navigieren, und eine, die verknüpfte Seitenzahlen bereitstellt. Wir arbeiten in dieser Komponente mit der ersteren.

// src/components/Pagination.js
import React from "react"
import { Link } from "gatsby"
import { blogURI } from "../../globals"


const Pagination = ({ pageNumber, hasNextPage }) => {
  if (pageNumber === 1 && !hasNextPage) return null


  return (
    <div style={{ margin: "60px auto 20px", textAlign: "center" }}>
      <div className="nav-links">
        {
          pageNumber > 1 && (
            <Link
              className="prev page-numbers"
              style={{
                padding: "8px 8px 5px 4px",
              }}
           to={pageNumber > 2 ? `${blogURI}/page/${pageNumber - 1}`: `${blogURI}/`}
            >
              ← <span> Previous</span>
            </Link>
          )
        }
          <span className="meta-nav screen-reader-text"></span>
          {pageNumber}
        </span>


        {
          hasNextPage && (
            <Link
              style={{
                padding: "4px 8px 5px 8px",
              }}
              className="next page-numbers"
              to={`${blogURI}/page/${pageNumber + 1}`
              }
            >
              <span>Next </span> →
            </Link>
          )
        }
      </div>
    </div>
  )
}


export default Pagination

In Zeile 7 gibt es eine Bedingung, die null zurückgibt, wenn pageNumber === 1 && !hasNextPage. Mit anderen Worten, wenn die hasPageNumber der aktuellen Seite größer als 1 ist, wird die Schaltfläche „Zurück“ (Zeilen 13-24) angezeigt. Ebenso wird die Schaltfläche „Weiter“ (Zeilen 30-42) angezeigt, wenn die hasNextPage der aktuellen Seite mindestens 1 ist.

Schritt 6: Refactoring von createPages

Wir müssen die Datei createPages.js bereinigen, um all die Arbeit zu reflektieren, die wir seit der Erstellung der Datei geleistet haben. Die Datei wird einfach zu groß mit allem, was sie verfolgt. Um unseren Code organisiert und strukturiert zu halten, können wir **GraphQL-Fragmente** verwenden, die es uns ermöglichen, „komplexe Abfragen in kleinere, leichter verständliche Komponenten aufzuteilen“, laut der Dokumentation.

GraphQL-Fragmente sind wiederverwendbare Einheiten, die es ermöglichen, Feldsätze zu konstruieren und sie dann in Abfragen einzubinden, wo immer sie benötigt werden.

Wenn wir Henrik's Anleitung befolgen, werden die GraphQL-Abfragefelder für die Beitrags-Vorlage und die Beitrags-Vorschau in der Datei data.js gespeichert.

// src/templates/posts/data.js
const PostTemplateFragment = `
  fragment PostTemplateFragment on WPGraphQL_Post {
    id
    postId
    title
    content
    link
    featuredImage {
      sourceUrl
    }
    categories {
      nodes {
        name
        slug
        id
      }
    }
    tags {
      nodes {
        slug
        name
        id
      }
    }
    author {
      name
      slug
    }
  }
`


const BlogPreviewFragment = `
  fragment BlogPreviewFragment on WPGraphQL_Post {
    id
    postId
    title
    uri
    date
    slug
    excerpt
    content
    featuredImage {
      sourceUrl
    }
    author {
      name
      slug
    }
  }
`


module.exports.PostTemplateFragment = PostTemplateFragment
module.exports.BlogPreviewFragment = BlogPreviewFragment

Als Nächstes erfordert das Refactoring der Datei create/createPosts.js, wie in der Anleitung beschrieben, das Hinzufügen des folgenden Codes am Anfang von createPosts.js (Zeilen 2-10), direkt über der const = GET_POSTS=`-Abfrageanweisung in Zeile 4.

// create/createPosts.js
const {
  PostTemplateFragment,
  BlogPreviewFragment,
} = require("../src/templates/posts/data.js")


const { blogURI } = require("../globals")


const postTemplate = require.resolve("../src/templates/posts/index.js")
const blogTemplate = require.resolve("../src/templates/posts/blog.js")


const GET_POSTS = `
  # Here we make use of the imported fragments which are referenced above
  ${PostTemplateFragment}
  ${BlogPreviewFragment}
  query GET_POSTS($first:Int $after:String) {
    wpgraphql {
      posts(
       first: $first
       after: $after
       # This will make sure to only get the parent nodes and no children
       where: {
         parent: null
       }
      ) {
         pageInfo {
           hasNextPage
           endCursor
         }
         nodes {
           uri


           # This is the fragment used for the Post Template
           ...PostTemplateFragment


           #This is the fragment used for the blog preview on archive pages
          ...BlogPreviewFragment
        }
      }
    }
 }
`

Hier werden die in den vorherigen Schritten erstellten Fragment-Strings (Zeilen 9-10) importiert und außerhalb der GET_POSTS-Abfrage (Zeile 12) registriert und als Fragmente (Zeilen 34 und 37) innerhalb der GET_POSTS($first:Int $after:String)-Abfrage verwendet.

Am Ende der Datei createPosts.js wird der Pfad blogPage mit der globalen Variablen blogURI (Zeilen 36-41) definiert und wir haben den Code zum Erstellen von paginierten Blog-Seiten hinzugefügt (Zeilen 99-111).

// create/createPosts.js
// Previous code excluded


const allPosts = []
const blogPages = [];
let pageNumber = 0;
const itemsPerPage = 10;


/** This is the export which Gatbsy will use to process.
 * @param { actions, graphql }
 * @returns {Promise<void>} */
module.exports = async ({ actions, graphql, reporter }, options) => {


  /** This is the method from Gatsby that we're going
   * to use to create posts in our static site */
  const { createPage } = actions


  /** Fetch posts method. This accepts variables to alter
   * the query. The variable `first` controls how many items to
   * request per fetch and the `after` controls where to start in
   * the dataset.
   * @param variables
   * @returns {Promise<*>} */
  const fetchPosts = async (variables) =>
    /** Fetch posts using the GET_POSTS query and the variables passed in */
    await graphql(GET_POSTS, variables).then(({ data }) => {
      /** Extract the data from the GraphQL query results */
      const {
        wpgraphql: {
          posts: {
            nodes,
            pageInfo: { hasNextPage, endCursor },
          },
        },
      } = data


      /** Define the path for the paginated blog page.
       * This is the url the page will live at
       * @type {string} */
      const blogPagePath = !variables.after
        ? `${blogURI}/`
        : `${blogURI}/page/${pageNumber + 1}`


      /** Add config for the blogPage to the blogPage array for creating later
       * @type {{
       *   path: string,
       *   component: string,
       *   context: {nodes: *, pageNumber: number, hasNextPage: *} }} */
      blogPages[pageNumber] = {
        path: blogPagePath,
        component: blogTemplate,
        context: {
          nodes,
          pageNumber: pageNumber + 1,
          hasNextPage,
          itemsPerPage,
          allPosts,
        },
      }


      /** Map over the posts for later creation */
      nodes
      && nodes.map((posts) => {
        allPosts.push(posts)
      })


     /** If there's another post, fetch more so we can have all the data we need */
      if (hasNextPage) {
        pageNumber++
        reporter.info(`fetch post ${pageNumber} of posts...`)
        return fetchPosts({ first: itemsPerPage, after: endCursor })
      }


      /** Once we're done, return all the posts so we can
       * create the necessary posts with all the data on hand */
      return allPosts
    })


  /** Kick off our `fetchPosts` method which will get us all
   * the posts we need to create individual posts */
  await fetchPosts({ first: itemsPerPage, after: null }).then((wpPosts) => {


    wpPosts && wpPosts.map((post) => {
      /** Build post path based of theme blogURI setting */
      const path = `${blogURI}${post.uri}`


      createPage({
        path: path,
        component: postTemplate,
        context: {
          post: post,
        },
      })


      reporter.info(`post created:  ${path}`)
    })


    reporter.info(`# -----> POSTS TOTAL: ${wpPosts.length}`)


    /** Map over the `blogPages` array to create the
     * paginated blog pages */
    blogPages
    && blogPages.map((blogPage) => {
      if (blogPage.context.pageNumber === 1) {
        blogPage.context.publisher = true;
        blogPage.context.label = blogPage.path.replace('/', '');
      }
      createPage(blogPage);
      reporter.info(`created blog archive page ${blogPage.context.pageNumber}`);
    });
  })
}

Die endgültigen aktualisierten Dateien create/createPosts.js und create/createPage.js sind in diesem GitHub-Repository verfügbar.

In seinem Tutorialbeitrag „Twenty Nineteen porting tutorial“ beschreibt Muhammad sehr detailliert, wie statische Seiten, die mit Gatsby's createPage erstellt werden, fast den gleichen Code und die gleiche Dateistruktur verwenden wie in diesem Beispiel. Es ist schön zu sehen, wie sich zwischen unseren Referenzen eine gewisse Konsistenz bildet.

Nachdem wir den lokalen Server mit gatsby develop neu gestartet haben, sollten wir nun einen Bildschirm in unserem Browser sehen, der eine Schleife unserer veröffentlichten Beiträge mit dem Beitragstitel und dem Auszug anzeigt.


Abschnitt 5: Styling und Bereitstellung

Obwohl Styling, Typografie und Bereitstellung über den Rahmen dessen hinausgehen, was wir hier behandeln, können wir sie kurz ansprechen. Die Dokumentation von Gatsby bietet ausgezeichnete Ressourcen sowohl für Styling als auch für Bereitstellungs-/Hosting-Optionen.

Grundlegendes Website-Styling

Die Dokumentation von Gatsby ist unterteilt in globale CSS-Dateien, modulare Stylesheets und CSS-in-JS. Es gibt weitere Styling-Optionen, darunter Typograpgy.js, Sass, JSS, Stylus und PostCSS.

Beim Portieren des Twenty Nineteen WordPress-Themes nach Gatsby fügt Muhammad die Stile des Themes hinzu, damit sie auf der Gatsby-Seite verwendet werden können. Er warnt jedoch, dass einige Anpassungen erforderlich sind, da einige Einheiten und Werte mit Gatsby nicht kompatibel sind. Zum Beispiel musste er vw-Einheiten in CSS anpassen, um sie für einige Komponenten mit Flexbox zu verwenden. Ebenso hat Henrik beim Portieren des Twenty Twenty-Themes nach Gatsby in seinem Gatsby-Starter – Twenty Twenty einen ähnlichen Prozess befolgt, indem er das Twenty Twenty Stylesheet sowie Schriften übernommen hat.

Ich habe mich entschieden, Sass in meinem Projekt zu verwenden. Das erfordert die Installation von gatsby-plugin-sass und seiner erforderlichen node-sass-Abhängigkeit.

#! install node-sass & gatsby-sass
yarn add node-sass gatsby-plugin-sass
#! or with npm
npm install --save node-sass gatsby-plugin-sass

Dann kann das Plugin zu gatsby-config.js hinzugefügt und wie hier gezeigt konfiguriert werden.

// gatsby-config.js
module.exports = {
  siteMetadata: {
    plugins: [
      `gatsby-plugin-sass`
    ],
  }
}

Jetzt können wir Stile in .scss-Dateien schreiben und sie wie in jedem anderen Sass-Projekt importieren.

// using import in a component file
import("./src/styles/global.scss")


// using require in the gatsby-browser.js file
require('./src/styles/global.scss')

Das .scss-Stylesheet kann von der globalen <Layout>-Komponente importiert oder in gatsby-browser.js mit einer require-Anweisung hinzugefügt werden. Für dieses Demo-Projekt verwende ich das Standard-Styling von Gatsby für die Hauptseite und habe den Beitragsinhalt einfach so belassen, wie er ist. Ich habe die Datei Header.js mit sehr grundlegendem Styling refactored.

//src/components/Header.js
import { Link } from "gatsby"
import PropTypes from "prop-types"
import React from "react"
import useSiteMetadata from '../components/siteMetadata';
import Menu from "./Menu"
import "../styles/header.css"


const Header = () =>{
  const { title } = useSiteMetadata();


  return (
    <header className="header">
      <div className="nav-container brand">
        <Link  to="/"> {title} </Link>
        {/* Menu here */}
        <Menu />
      </div>
    </header>
  )
}


Header.propTypes = {
  siteTitle: PropTypes.string,
  description: PropTypes.string,
}


Header.defaultProps = {
  siteTitle: ``,
  description: ``,
}


export default Header

Dies sollte uns den Website-Header anzeigen, wenn wir den Server mit gatsby develop neu starten.

Unterstützung von WordPress-Blockstilen

Ich gehe davon aus, dass Sie mit dem WordPress-Blockeditor gut vertraut sind, wenn Sie es bis hierher geschafft haben, und wissen, wie Blöcke im Allgemeinen funktionieren. Seit der Veröffentlichung des Blockeditors pflegt WordPress einen separaten Satz von Stilen speziell für Blockinhalte.

Das bedeutet, dass wir einen zusätzlichen Schritt benötigen, um diese mit den Theme-Stilen nach Gatsby zu portieren. Jason Lengstorf demonstriert dies in seinem Tutorial-Leitfaden. Zuerst wird das WordPress-Blocks-Paket installiert.

# install wordpress/block-library
npm install @wordpress/block-library
# with yarn add
yarn add @wordpress/block-library

Dann können wir diese Stile in eine Gatsby-Komponente importieren. Nehmen wir die <Layout>-Komponente.

// src/components/layout.js
import React from "react"
  import { Link } from "gatsby"


import "@wordpress/block-library/build-style/style.css"
  import "../styles/layout.css"


const Layout = ({ children }) => {
  return (
    <section>
      <header>
        <Link to="/" className="home">
          Gatsby + WP
        </Link>
      </header>
      <main>{children}</main>
    </section>
  )
}


export default Layout

Der Blockeditor befindet sich noch in aktiver Entwicklung, was bedeutet, dass Dinge anfällig für Änderungen sind, möglicherweise unerwartet. Gehen Sie also unbedingt mit Vorsicht vor, wenn Sie planen, sie zu verwenden.

Website-Bereitstellung

Wir haben ein wenig über die Bereitstellung gesprochen, als ich erklärt habe, warum ich mich für Netlify entschieden habe. Ich habe mich dafür entschieden, weil es mit dem GitHub-Repository des Projekts verknüpft ist und dank Netlify Functions automatisch bereitgestellt wird, wenn man in einen bestimmten Branch pusht.

Netlify bietet eine gute Schritt-für-Schritt-Anleitung, die beschreibt, wie eine Gatsby-Site mit Netlify verbunden wird. Die Gatsby-Dokumentation beschreibt auch die Bereitstellung auf Netlify.

Abschließend ein Link zu meiner eigenen, auf Netlify bereitgestellten Demo-Website.

Auch hier erhalten wir Continuous Deployment, bei dem die Website automatisch neu erstellt wird, wenn Änderungen in das Repository gepusht werden. Wenn wir einen ähnlichen Prozess wünschen, wann immer Änderungen in WordPress vorgenommen werden (z. B. ein Beitrag veröffentlicht oder eine Seite bearbeitet wird), dann kann das JAMstack Deployments Plugin verwendet werden, wie in Jason's Anleitung beschrieben.


Dies ist noch in Arbeit!

Während das, was ich im Prozess des Portierens eines WordPress-Themes nach Gatsby gelernt habe, großartig für den Aufbau der grundlegenden Bausteine eines Blogs ist, habe ich erkannt, dass noch viel Arbeit zu erledigen ist. Ich meine, WordPress speichert so viele Daten, darunter Autoren, Kategorien, Tags, Beitragsstatus, benutzerdefinierte Beitragstypen und vieles mehr, die alle zusätzliche Überlegung erfordern.

Aber es gibt eine wachsende Liste von entkoppelten Gatsby-WordPress-Website-Beispielen, von denen einige ich unten als Referenz auflisten werde. Henrik's eine großartige Liste von WordPress-Gatsby-Ressourcen ist sehr hilfreich, um mehr über die Entkopplung von WordPress-Gatsby zu erfahren.

Danksagungen

Ich weiß, dass ich es während dieses Beitrags erwähnt habe, aber ein großes Lob an Henrick Wirth, Jason Lengstorf und Muhammad Muhsin für all die Arbeit, die sie geleistet haben, um zu dokumentieren und zu teilen, was es braucht, um WordPress nach Gatsby zu portieren. Alles, was ich hier behandelt habe, ist lediglich die Akkumulation ihrer hervorragenden Arbeit, und ich schätze jeden von ihnen dafür, dass sie so hilfreiche Anleitungen erstellt haben, die selbst für Anfänger wie mich geeignet sind. Ich danke auch unserem eigenen Geoff Graham von CSS-Tricks für die Bearbeitung dieses Artikels.