Haben Sie schon einmal versucht, WordPress headless mit Gatsby zu verwenden? Wenn nicht, könnten Sie sich diesen Artikel über das neue Gatsby Source Plugin für WordPress ansehen; gatsby-source-wordpress ist das offizielle Quell-Plugin, das im März 2021 als Teil des Gatsby 3-Releases eingeführt wurde. Es verbessert die Integration mit WordPress erheblich. Außerdem ist das WordPress-Plugin WPGraphQL, das die GraphQL-API bereitstellt, jetzt über das offizielle WordPress-Repository verfügbar.
Mit stabilen und gepflegten Tools wird die Entwicklung von Gatsby-Websites, die von WordPress betrieben werden, einfacher und interessanter. Ich habe mich diesem Bereich gewidmet, ich habe Alexandra Spalato mitbegründet und kürzlich Gatsby WP Themes gestartet – ein Nischenmarktplatz für Entwickler, die WordPress-gestützte Websites mit Gatsby erstellen. In diesem Artikel möchte ich meine Erkenntnisse teilen und insbesondere die Suchfunktionalität diskutieren.
Die Suche ist nicht standardmäßig enthalten, aber es gibt viele Optionen zu berücksichtigen. Ich werde mich auf zwei verschiedene Möglichkeiten konzentrieren – die Nutzung der nativen WordPress-Suche (WordPress-Suchanfrage) im Vergleich zur Verwendung von Jetpack Instant Search.
Erste Schritte
Beginnen wir mit der Einrichtung einer WordPress-gestützten Gatsby-Website. Der Einfachheit halber folge ich den Anweisungen zum Einstieg und installiere den gatsby-starter-wordpress-blog Starter.
gatsby new gatsby-wordpress-w-search https://github.com/gatsbyjs/gatsby-starter-wordpress-blog
Dieser einfache, grundlegende Starter erstellt Routen ausschließlich für einzelne Beiträge und Blogseiten. Aber wir können es hier so einfach halten. Stellen wir uns vor, wir möchten keine Seiten in den Suchergebnissen berücksichtigen.
Vorerst lasse ich die WordPress-Quell-Website so, wie sie ist, und ziehe den Inhalt aus dem WordPress-Demo des Autors des Starters. Wenn Sie Ihre eigene Quelle verwenden, denken Sie daran, dass auf der WordPress-Seite zwei Plugins erforderlich sind (beide über das Plugin-Repository verfügbar)
- WPGraphQL – ein Plugin, das einen GraphQL-Server auf der WordPress-Instanz ausführt
- WPGatsby – ein Plugin, das das WPGraphQL-Schema auf Gatsby-spezifische Weise modifiziert (es fügt auch einen Mechanismus hinzu, um den Build-Prozess zu optimieren)
Apollo Client einrichten
Mit Gatsby verwenden wir normalerweise entweder die Daten aus Abfragen, die bei der Seitenerstellung ausgeführt werden (Seitenabfragen), oder wir rufen den Hook useStaticQuery auf. Letzterer ist in Komponenten verfügbar und erlaubt keine dynamischen Abfrageparameter; seine Aufgabe ist es, GraphQL-Daten zur Build-Zeit abzurufen. Keine dieser beiden Abfragelösungen funktioniert für eine vom Benutzer initiierte Suche. Stattdessen bitten wir WordPress, eine Suchanfrage auszuführen und uns die Ergebnisse zurückzusenden. Können wir eine GraphQL-Suchanfrage senden? Ja! WPGraphQL bietet Suche; Sie können Beiträge in WPGraphQL wie folgt durchsuchen
posts(where: {search: "gallery"}) {
nodes {
id
title
content
}
}
Um direkt mit unserer WPGraphQL-API zu kommunizieren, installieren wir Apollo Client; er kümmert sich um die Anforderung und das Caching der Daten sowie um die Aktualisierung unserer UI-Komponenten.
yarn add @apollo/client cross-fetch
Um auf Apollo Client in unserem Komponentenbaum zugreifen zu können, müssen wir unsere App mit ApolloProvider umschließen. Gatsby stellt die App-Komponente, die die gesamte Anwendung umschließt, nicht bereit. Stattdessen bietet es die wrapRootElement API. Dies ist Teil der Gatsby Browser API und muss in der Datei gatsby-browser.js im Stammverzeichnis des Projekts implementiert werden.
// gatsby-browser.js
import React from "react"
import fetch from "cross-fetch"
import { ApolloClient, HttpLink, InMemoryCache, ApolloProvider } from "@apollo/client"
const cache = new InMemoryCache()
const link = new HttpLink({
/* Set the endpoint for your GraphQL server, (same as in gatsby-config.js) */
uri: "https://wpgatsbydemo.wpengine.com/graphql",
/* Use fetch from cross-fetch to provide replacement for server environment */
fetch
})
const client = new ApolloClient({
link,
cache,
})
export const wrapRootElement = ({ element }) => (
<ApolloProvider client={client}>{element}</ApolloProvider>
)
SearchForm-Komponente
Nachdem wir ApolloClient eingerichtet haben, erstellen wir nun unsere Search-Komponente.
touch src/components/search.js src/components/search-form.js src/components/search-results.js src/css/search.css
Die Komponente Search umschließt SearchForm und SearchResults
// src/components/search.js
import React, { useState } from "react"
import SearchForm from "./search-form"
import SearchResults from "./search-results"
const Search = () => {
const [searchTerm, setSearchTerm] = useState("")
return (
<div className="search-container">
<SearchForm setSearchTerm={setSearchTerm} />
{searchTerm && <SearchResults searchTerm={searchTerm} />}
</div>
)
}
export default Search
<SearchForm /> ist ein einfaches Formular mit gesteuertem Input und einem Submit-Handler, der den searchTerm-Zustandswert auf die Benutzereingabe setzt.
// src/components/search-form.js
import React, { useState } from "react"
const SearchForm = ({ searchTerm, setSearchTerm }) => {
const [value, setValue] = useState(searchTerm)
const handleSubmit = e => {
e.preventDefault()
setSearchTerm(value)
}
return (
<form role="search" onSubmit={handleSubmit}>
<label htmlFor="search">Search blog posts:</label>
<input
id="search"
type="search"
value={value}
onChange={e => setValue(e.target.value)}
/>
<button type="submit">Submit</button>
</form>
)
}
export default SearchForm
Die Komponente SearchResults empfängt den searchTerm über Props, und dort verwenden wir Apollo Client.
Für jeden searchTerm möchten wir die passenden Beiträge als Liste anzeigen, die den Titel, den Auszug und einen Link zu diesem einzelnen Beitrag enthält. Unsere Abfrage wird wie folgt aussehen
const GET_RESULTS = gql`
query($searchTerm: String) {
posts(where: { search: $searchTerm }) {
edges {
node {
id
uri
title
excerpt
}
}
}
}
`
Wir verwenden den Hook useQuery von @apollo-client, um die GET_RESULTS-Abfrage mit einer Suchvariable auszuführen.
// src/components/search-results.js
import React from "react"
import { Link } from "gatsby"
import { useQuery, gql } from "@apollo/client"
const GET_RESULTS = gql`
query($searchTerm: String) {
posts(where: { search: $searchTerm }) {
edges {
node {
id
uri
title
excerpt
}
}
}
}
`
const SearchResults = ({ searchTerm }) => {
const { data, loading, error } = useQuery(GET_RESULTS, {
variables: { searchTerm }
})
if (loading) return <p>Searching posts for {searchTerm}...</p>
if (error) return <p>Error - {error.message}</p>
return (
<section className="search-results">
<h2>Found {data.posts.edges.length} results for {searchTerm}:</h2>
<ul>
{data.posts.edges.map(el => {
return (
<li key={el.node.id}>
<Link to={el.node.uri}>{el.node.title}</Link>
</li>
)
})}
</ul>
</section>
)
}
export default SearchResults
Der Hook useQuery gibt ein Objekt zurück, das die Eigenschaften loading, error und data enthält. Wir können verschiedene UI-Elemente je nach Zustand der Abfrage rendern. Solange loading wahr ist, zeigen wir <p>Searching posts...</p> an. Wenn loading und error beide falsch sind, ist die Abfrage abgeschlossen und wir können über data.posts.edges iterieren und die Ergebnisse anzeigen.
if (loading) return <p>Searching posts...</p>
if (error) return <p>Error - {error.message}</p>
// else
return ( //... )
Vorerst füge ich die <Search /> zur Layout-Komponente hinzu. (Ich werde sie später woanders hin verschieben.) Dann, mit etwas Styling und einer visible-Zustandsvariable, habe ich es eher wie ein Widget gestaltet, das sich per Klick öffnet und oben rechts fixiert ist.
Paginierte Abfragen
Ohne Angabe der Anzahl der Einträge gibt die WPGraphQL-Beitragsabfrage die ersten zehn Beiträge zurück; wir müssen uns um die Paginierung kümmern. WPGraphQL implementiert die Paginierung nach der Relay Specification für GraphQL-Schema-Design. Ich werde nicht ins Detail gehen; merken wir uns einfach, dass dies ein standardisiertes Muster ist. Innerhalb der Relay-Spezifikation haben wir neben posts.edges (einer Liste von { cursor, node }-Objekten) Zugriff auf das Objekt posts.pageInfo, das Folgendes bereitstellt:
endCursor– Cursor des letzten Elements inposts.edges,startCursor– Cursor des ersten Elements inposts.edges,hasPreviousPage– boolescher Wert für „Gibt es weitere verfügbare Ergebnisse (rückwärts)?“, undhasNextPage– boolescher Wert für „Gibt es weitere verfügbare Ergebnisse (vorwärts)?“.
Wir können den Datenbereich, auf den wir zugreifen möchten, mit zusätzlichen Abfragevariablen ändern
first– die Anzahl der zurückgegebenen Einträgeafter– der Cursor, nach dem wir beginnen sollen
Wie gehen wir mit paginierten Abfragen mit Apollo Client um? Der empfohlene Ansatz ist die Verwendung der fetchMore-Funktion, die (zusammen mit loading, error und data) Teil des von useQuery zurückgegebenen Objekts ist.
// src/components/search-results.js
import React from "react"
import { Link } from "gatsby"
import { useQuery, gql } from "@apollo/client"
const GET_RESULTS = gql`
query($searchTerm: String, $after: String) {
posts(first: 10, after: $after, where: { search: $searchTerm }) {
edges {
node {
id
uri
title
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
`
const SearchResults = ({ searchTerm }) => {
const { data, loading, error, fetchMore } = useQuery(GET_RESULTS, {
variables: { searchTerm, after: "" },
})
if (loading && !data) return <p>Searching posts for {searchTerm}...</p>
if (error) return <p>Error - {error.message}</p>
const loadMore = () => {
fetchMore({
variables: {
after: data.posts.pageInfo.endCursor,
},
// with notifyOnNetworkStatusChange our component re-renders while a refetch is in flight so that we can mark loading state when waiting for more results (see lines 42, 43)
notifyOnNetworkStatusChange: true,
})
}
return (
<section className="search-results">
{/* as before */}
{data.posts.pageInfo.hasNextPage && (
<button type="button" onClick={loadMore} disabled={loading}>
{loading ? "Loading..." : "More results"}
</button>
)}
</section>
)
}
export default SearchResults
Das Argument first hat einen Standardwert, ist aber hier **notwendig**, um anzuzeigen, dass wir eine paginierte Anfrage senden. Ohne first wird pageInfo.hasNextPage immer false sein, unabhängig vom Suchbegriff.
Das Aufrufen von fetchMore ruft den nächsten Datensatz ab, aber wir müssen Apollo noch mitteilen, wie es die „mehr abgerufenen“ Ergebnisse zusammenführen soll mit den bestehenden gecachten Daten. Wir legen die gesamte Paginierungslogik an einer zentralen Stelle als Option für den InMemoryCache-Konstruktor fest (in der Datei gatsby-browser.js). Und raten Sie mal? Mit der Relay-Spezifikation haben wir das abgedeckt – Apollo Client bietet die Funktion relayStylePagination, die all die Magie für uns erledigt.
// gatsby-browser.js
import { ApolloClient, HttpLink, InMemoryCache, ApolloProvider } from "@apollo/client"
import { relayStylePagination } from "@apollo/client/utilities"
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
posts: relayStylePagination(["where"]),
},
},
},
})
/* as before */
Nur ein wichtiges Detail: Wir paginieren nicht alle Beiträge, sondern die Beiträge, die einer bestimmten where-Bedingung entsprechen. Das Hinzufügen von ["where"] als Argument zu relayStylePagination erstellt einen eindeutigen Speicherschlüssel für verschiedene Suchbegriffe.
Suchergebnisse persistent machen
Im Moment befindet sich meine Search-Komponente in der Layout-Komponente. Sie wird auf jeder Seite angezeigt, aber bei jedem Routenwechsel demontiert. Was wäre, wenn wir die Suchergebnisse beim Navigieren beibehalten könnten? Wir können die Gatsby wrapPageElement Browser-API nutzen, um persistente UI-Elemente um die Seiten herum zu setzen.
Verschieben wir <Search /> von der Layout-Komponente in die wrapPageElement
// gatsby-browser.js
import Search from "./src/components/search"
/* as before */
export const wrapPageElement = ({ element }) => {
return <><Search />{element}</>
}
Die APIs wrapPageElement und wrapRootElement existieren sowohl im Browser als auch in den Server-Side Rendering (SSR) APIs. Gatsby empfiehlt, dass wirwrapPageElement und wrapRootElement sowohl in gatsby-browser.js als auch in gatsby-ssr.js implementieren. Erstellen wir diegatsby-ssr.js (im Stammverzeichnis des Projekts) und exportieren unsere Elemente erneut
// gatsby-ssr.js
export { wrapRootElement, wrapPageElement } from "./gatsby-browser"
Ich habe eine Demo bereitgestellt, in der Sie es in Aktion sehen können. Sie können den Code auch in diesem Repository finden.
Der Ansatz mit wrapPageElement ist möglicherweise nicht in allen Fällen ideal. Unser Such-Widget ist vom Layout-Komponente „entkoppelt“. Es funktioniert gut mit der Position „fixed“ wie in unserem funktionierenden Beispiel oder in einer Off-Canvas-Seitenleiste wie in diesem Gatsby WordPress-Theme.
Aber was, wenn Sie „persistente“ Suchergebnisse in einer „klassischen“ Seitenleiste anzeigen möchten? In diesem Fall könnten Sie den searchTerm-Zustand von der Search-Komponente in einen Suchkontext-Provider verschieben, der innerhalb von wrapRootElement platziert ist.
// gatsby-browser.js
import SearchContextProvider from "./src/search-context"
/* as before */
export const wrapRootElement = ({ element }) => (
<ApolloProvider client={client}>
<SearchContextProvider>
{element}
</SearchContextProvider>
</ApolloProvider>
)
…mit dem SearchContextProvider, der wie folgt definiert ist:
// src/search-context.js
import React, {createContext, useState} from "react"
export const SearchContext = createContext()
export const SearchContextProvider = ({ children }) => {
const [searchTerm, setSearchTerm] = useState("")
return (
<SearchContext.Provider value={{ searchTerm, setSearchTerm }}>
{children}
</SearchContext.Provider>
)
}
Sie können es in einem anderen Gatsby WordPress-Theme in Aktion sehen
Beachten Sie, wie wir dank des Caching der Suchergebnisse durch Apollo Client diese sofort beim Routenwechsel erhalten.
Ergebnisse von Beiträgen und Seiten
Wenn Sie sich die obigen Theme-Beispiele angesehen haben, ist Ihnen vielleicht aufgefallen, wie ich mit der Abfrage von mehr als nur Beiträgen umgehe. Mein Ansatz ist, die gleiche Logik für Seiten zu replizieren und Ergebnisse für jeden Beitragstyp separat anzuzeigen.
Alternativ könnten Sie die Content Node-Schnittstelle verwenden, um Knoten verschiedener Beitragstypen in einer einzigen Verbindung abzufragen.
const GET_RESULTS = gql`
query($searchTerm: String, $after: String) {
contentNodes(first: 10, after: $after, where: { search: $searchTerm }) {
edges {
node {
id
uri
... on Page {
title
}
... on Post {
title
excerpt
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
`
Jenseits der Standard-WordPress-Suche
Unsere Lösung scheint zu funktionieren, aber denken wir daran, dass der zugrunde liegende Mechanismus, der die Suche für uns tatsächlich *durchführt*, die native WordPress-Suchanfrage ist. Und die Standard-Suchfunktion von WordPress ist nicht gut. Ihre Probleme sind begrenzte Suchfelder (insbesondere werden Taxonomien nicht berücksichtigt), kein Fuzzy-Matching, keine Kontrolle über die Reihenfolge der Ergebnisse. Große Websites können auch unter Performance-Problemen leiden – es gibt keinen vordefinierten Suchindex, und die Suchanfrage wird direkt auf der SQL-Datenbank der Website ausgeführt.
Es gibt einige WordPress-Plugins, die die Standardsuche verbessern. Plugins wie WP Extended Search fügen die Möglichkeit hinzu, ausgewählte Metaschlüssel und Taxonomien in Suchanfragen einzubeziehen.
Das Plugin Relevanssi ersetzt die Standard-WordPress-Suche durch seine Suchmaschine, die die Full-Text-Indexing-Fähigkeiten der Datenbank nutzt. Relevanssi deaktiviert die Standard-Suchanfrage, was die WPGraphQL-Abfrage where: {search : …} unterbricht. Es wurden bereits einige Arbeiten durchgeführt, um die Relevanssi-Suche über WPGraphQL zu ermöglichen; der Code ist möglicherweise nicht mit der neuesten WPGraphQL-Version kompatibel, aber er scheint ein guter Anfang für diejenigen zu sein, die sich für die Relevanssi-Suche entscheiden.
Im zweiten Teil dieses Artikels werden wir einen weiteren möglichen Weg einschlagen und uns den Premium-Dienst von Jetpack genauer ansehen – eine erweiterte Suche, die von Elasticsearch unterstützt wird. Übrigens, Jetpack Instant Search ist die von CSS-Tricks übernommene Lösung.
Jetpack Instant Search mit Gatsby verwenden
Jetpack Search ist eine Premium-Lösung pro Website von Jetpack. Sobald sie installiert und aktiviert ist, kümmert sie sich um den Aufbau eines Elasticsearch-Index. Die Suchanfragen greifen nicht mehr auf die SQL-Datenbank zu. Stattdessen werden die Suchanfragen an den Cloud-Elasticsearch-Server gesendet, genauer gesagt an
https://public-api.wordpress.com/rest/v1.3/sites/{your-blog-id}/search
Es gibt viele Suchparameter, die in der obigen URL angegeben werden müssen. In unserem Fall fügen wir Folgendes hinzu:
filter[bool][must][0][term][post_type]=post: Wir benötigen hier nur Beiträge, da unsere Gatsby-Website auf Beiträge beschränkt ist. Im realen Leben müssen Sie möglicherweise etwas Zeit darauf verwenden, Boolesche Abfragen zu konfigurieren.size=10legt die Anzahl der zurückgegebenen Ergebnisse fest (maximal 20).- Mit
highlight_fields[0]=titleerhalten wir den Titel-String (oder einen Teil davon) mit demsearchTerminnerhalb der<mark>-Tags. highlight_fields[0]=contentist dasselbe wie unten, aber für den Inhalt des Beitrags.
Es gibt drei weitere Suchparameter, die vom Benutzeraktion abhängen
query: Der Suchbegriff aus dem Suchfeld, z.B. Galeriesort: wie die Ergebnisse sortiert werden sollen, Standard ist nach Relevanz"score_default", aber es gibt auch"date_asc"(neueste) und"date_desc"(älteste)page_handle: so etwas wie der „after“-Cursor für paginierte Ergebnisse. Wir fordern nur 10 Ergebnisse auf einmal an und haben einen „Mehr laden“-Button.
Sehen wir uns nun an, wie eine erfolgreiche Antwort strukturiert ist
{
total: 9,
corrected_query: false,
page_handle: false, // or a string it the total value > 10
results: [
{
_score: 196.51814,
fields: {
date: '2018-11-03 03:55:09',
'title.default': 'Block: Gallery',
'excerpt.default': '',
post_id: 1918,
// we can configure what fields we want to add here with the query search parameters
},
result_type: 'post',
railcar: {/* we will not use this data */},
highlight: {
title: ['Block: <mark>Gallery</mark>'],
content: [
'automatically stretch to the width of your <mark>gallery</mark>. ... A four column <mark>gallery</mark> with a wide width:',
'<mark>Gallery</mark> blocks have two settings: the number of columns, and whether or not images should be cropped',
],
},
},
/* more results */
],
suggestions: [], // we will not use suggestions here
aggregations: [], // nor the aggregations
}
Das Feld results liefert ein Array mit den Datenbank-Beitrags-IDs. Um die Suchergebnisse auf einer Gatsby-Site anzuzeigen, müssen wir die entsprechenden Beitrags-Nodes (insbesondere deren uri) aus der Gatsby-Datenschicht extrahieren. Mein Ansatz ist die Implementierung einer Sofortsuche mit asynchronen Aufrufen an die REST-API und der Überlagerung der Ergebnisse mit denen der statischen GraphQL-Abfrage, die **alle Beitrags-Nodes** zurückgibt.
Beginnen wir mit dem Erstellen eines Sofort-Such-Widgets, das mit der Such-API kommuniziert. Da dies nicht spezifisch für Gatsby ist, sehen wir es in diesem Pen in Aktion
Hier ist useDebouncedInstantSearch ein benutzerdefinierter Hook, der für das Abrufen der Ergebnisse von der Jetpack Search API zuständig ist. Meine Lösung verwendet die Bibliothek awesome-debounce-promise, die uns erlaubt, den Fetching-Mechanismus etwas genauer zu handhaben. Eine Sofortsuche reagiert direkt auf die Eingabe, ohne auf ein explizites „Go!“ vom Benutzer zu warten. Wenn ich schnell tippe, kann sich die Anfrage mehrmals ändern, bevor die erste Antwort eintrifft. Daher kann es zu unnötigem Verschleiß der Bandbreite kommen. awesome-debounce-promise wartet ein bestimmtes Zeitintervall (z.B. 300ms), bevor es einen API-Aufruf tätigt; wenn innerhalb dieses Intervalls ein neuer Aufruf erfolgt, wird der vorherige niemals ausgeführt. Es löst auch nur das letzte von dem Aufruf zurückgegebene Promise auf – dies verhindert Nebenläufigkeitsprobleme.
Nun, da die Suchergebnisse verfügbar sind, kehren wir zu Gatsby zurück und erstellen einen weiteren benutzerdefinierten Hook
import {useStaticQuery, graphql} from "gatsby"
export const useJetpackSearch = (params) => {
const {
allWpPost: { nodes },
} = useStaticQuery(graphql`
query AllPostsQuery {
allWpPost {
nodes {
id
databaseId
uri
title
excerpt
}
}
}
`)
const { error, loading, data } = useDebouncedInstantSearch(params)
return {
error,
loading,
data: {
...data,
// map the results
results: data.results.map(el => {
// for each result find a node that has the same databaseId as the result field post_id
const node = nodes.find(item => item.databaseId === el.fields.post_id)
return {
// spread the node
...node,
// keep the highlight info
highlight: el.highlight
}
}),
}
}
}
Ich werde useJetpackSearch innerhalb von <SearchResults /> aufrufen. Die Gatsby-Version von <SearchResults /> ist fast identisch mit der im obigen Pen. Die Unterschiede sind im folgenden Codeblock hervorgehoben. Der Hook useDebouncedInstantSearch wird durch useJetpackSearch ersetzt (der erstere intern aufruft). Ein Gatsby Link ersetzt h2, und el.fields["title.default"] und el.fields["excerpt.default"] werden durch el.title und el.excerpt ersetzt.
const SearchResults = ({ params, setParams }) => {
const { loading, error, data } = useJetpackSearch(params)
const { searchTerm } = params
if (error) {
return <p>Error - {error}</p>
}
return (
<section className="search-results">
{loading ? (
<p className="info">Searching posts .....</p>
) : (
<>
{data.total !== undefined && (
<p>
Found {data.total} results for{" "}
{data.corrected_query ? (
<>
<del>{searchTerm}</del> <span>{data.corrected_query}</span>
</>
) : (
<span>{searchTerm}</span>
)}
</p>
)}
</>
)}
{data.results?.length > 0 && (
<ul>
{data.results.map((el) => {
return (
<li key={el.id}>
<Link to={el.uri}>
{el.highlight.title[0]
? el.highlight.title.map((item, index) => (
<React.Fragment key={index}>
{parse(item)}
</React.Fragment>
))
: parse(el.title)}
</Link>
<div className="post-excerpt">
{el.highlight.content[0]
? el.highlight.content.map((item, index) => (
<div key={index}>{parse(item)}</div>
))
: parse(el.excerpt)}
</div>
</li>
);
})}
</ul>
)}
{data.page_handle && (
<button
type="button"
disabled={loading}
onClick={() => setParams({ pageHandle: data.page_handle })}
>
{loading ? "loading..." : "load more"}
</button>
)}
</section>
)
}
Den vollständigen Code finden Sie in diesem Repository und sehen Sie ihn in Aktion in dieser Demo. Beachten Sie, dass ich keine WordPress-Daten mehr aus dem generischen WordPress-Demo beziehe, das vom Gatsby-Starter verwendet wird. Ich benötige eine Website mit aktiviertem Jetpack Search.
Zusammenfassung
Wir haben gerade zwei Möglichkeiten zur Behandlung von Suchen in headless WordPress gesehen. Abgesehen von einigen Gatsby-spezifischen technischen Details (wie der Verwendung der Gatsby Browser API) können Sie beide diskutierten Ansätze auch in anderen Frameworks implementieren. Wir haben gesehen, wie man die native WordPress-Suche nutzt. Ich vermute, dass dies in vielen Fällen eine akzeptable Lösung ist.
Wenn Sie jedoch etwas Besseres brauchen, gibt es bessere Optionen. Eine davon ist Jetpack Search. Jetpack Instant Search leistet hervorragende Arbeit auf CSS-Tricks und kann, wie wir gerade gesehen haben, auch mit headless WordPress funktionieren. Es gibt wahrscheinlich noch andere Möglichkeiten, es zu implementieren. Sie können auch weiter mit der Konfiguration von Abfragen, den Filterfunktionen und der Art und Weise, wie Sie die Ergebnisse anzeigen, gehen.