
GuideSanity's neues Preview-Kit für Echtzeit-Vorschauen mit GatsbyJS nutzen
Willkommen zu diesem Leitfaden über die Nutzung der Möglichkeiten von Sanity's brandneuem Preview-Kit für die Erstellung von Echtzeit-Vorschauen für Ihre GatsbyJS-Projekte. Es ist ein bemerkenswertes Toolkit, das darauf ausgelegt ist, dynamische Content-Vorschau-Erfahrungen mit Live-Tipp-Funktionalität und visuellen Bearbeitungsfähigkeiten zu gestalten. Frisch veröffentlicht, stattet es Entwickler wie Sie mit den Werkzeugen aus, um Ihre Vorschau-Erfahrungen auf das nächste Level zu heben, und ermöglicht dabei Live- und Echtzeit-Vorschauen, die speziell auf React-basierte Projekte zugeschnitten sind.
In diesem Leitfaden führen wir Sie durch den Prozess der nahtlosen Integration von Sanity's preview-kit mit GatsbyJS und dem Deployment auf Netlify, damit Sie Ihren Benutzern eine fließende und interaktive Content-Vorschau-Umgebung anbieten können. Tauchen wir ein und erkunden wir, wie Sie Ihren Content-Creation-Workflow mit diesem hochmodernen Toolkit revolutionieren können.
Wenn Sie an irgendeinem Punkt Unterstützung bei der Implementierung oder Erweiterung der hier behandelten Konzepte benötigen, zögern Sie nicht, mich zu kontaktieren. Ich helfe gerne weiter.
Voraussetzungen
Bevor wir fortfahren, hier unsere Annahmen:
Sie haben einen Sanity-Account.
Sie sind mit Sanity Studio und Gatsby vertraut.
Sie haben Kenntnisse in JavaScript, React und der Kommandozeile.
Die Ergebnisse: Was wir aufbauen
In diesem Abschnitt schauen wir uns an, was wir mit diesem Leitfaden erreichen möchten und an welchem Projekt wir arbeiten werden.
Projektüberblick: Unser Ziel ist es, eine einfache Blog-Website zu erstellen, die als praktisches Beispiel für die Integration von Echtzeit-Vorschau-Fähigkeiten dient. Um den Prozess zu visualisieren, stellen wir relevante Screenshots und Visuals zur Verfügung.
Ziele:
Echtzeit-Bearbeitungserfahrung: Redakteure sollen sofortiges Echtzeit-Feedback erhalten und die Verzögerung zwischen Änderungen und deren Auswirkungen minimieren.
Reibungslose Bearbeitung: Wir möchten den Bedarf an Vorschau-Rebuilds auf der Gatsby-Seite eliminieren. Dies ermöglicht einen effizienten Content-Building-Loop.
Vorschau im Split-Pane: Die Echtzeit-Vorschau der Webseite wird nahtlos in Sanity Studio integriert. Wir erreichen dies durch die Verwendung eines iFrames innerhalb der Studio-Oberfläche.
Benutzerdefinierte Vorschau-Komponenten: Redakteure werden durch Warnungen und Ladehinweise informiert, damit sie sicher arbeiten können.
Ein neues Sanity-Blog-Projekt erstellen
Tauchen wir in den spannenden Prozess der Erstellung eines neuen Sanity-Projekts von Grund auf ein.
Wenn Sie bereits mit den Grundlagen vertraut sind, können Sie direkt zum Abschnitt "Vorschauen sowohl im Sanity Studio als auch im Gatsby-Frontend-Code implementieren" springen, wo wir uns den Feinheiten der Einrichtung von Vorschauen im Sanity Studio und im Gatsby-Frontend-Code widmen.
So geht es los:
Projektordner erstellen: Öffnen Sie Ihr Terminal und erstellen Sie einen neuen Ordner für Ihr Projekt mit folgendem Befehl: mkdir sanity-live-preview && cd sanity-live-preview
Ein neues Sanity-Projekt initiieren: Initiieren Sie nun mit npm ein neues Sanity-Projekt, indem Sie den unten stehenden Befehl ausführen. Dieser Befehl richtet das Projekt mit einer für eine Blog-Website geeigneten Vorlage ein, komplett mit vordefinierten Schemas für "post": npm create sanity@latest -- --template blog --create-project "Sanity Live Preview" --dataset production
Befehlsübersicht:
--template blog: Fügt notwendige Schemas wie "post" und "author" hinzu.
--create-project "Sanity Live Previews with Gatsby": Benennt das Projekt "Sanity Live Previews with Gatsby".
--dataset production: Setzt den Standard-Datensatz des Projekts auf production (kann später bei Bedarf angepasst werden).
Folgen Sie den Eingabeaufforderungen während der Installation und fügen Sie die erforderlichen Einstellungen hinzu.
Sobald die Installation abgeschlossen ist, führen Sie den folgenden Befehl aus, um das Sanity Studio zu starten:
npx sanity dev
Dies richtet eine lokale Instanz von Sanity Studio ein, die unter http://localhost:3333 erreichbar ist.
Mit diesen Schritten haben Sie ein neues Sanity-Projekt für Ihren Blog initiiert. Wir sind nun bereit, in den Implementierungsprozess einzutauchen.
Inhalte zu Ihrem Sanity Studio hinzufügen
Bevor wir fortfahren, müssen wir einige Inhalte hinzufügen. Erstellen wir ein paar Beiträge, um Ihren Blog zu befüllen.
Um einen Beitrag zu erstellen, können Sie zum Sanity Studio unter http://localhost:3333 navigieren, wo Sie einen eigenen Bereich für die Verwaltung Ihrer Blog-Inhalte finden. In diesem Bereich können Sie auf das Element "Posts" klicken. Von dort aus können Sie auf die Schaltfläche "Create New" klicken, um einen neuen Beitrag zu erstellen.
Im Beitragsformular finden Sie Felder für Titel, Inhalt, Hauptbild, Kategorie, Tags und andere relevante Informationen für Ihren Blog-Beitrag. Füllen Sie die erforderlichen Felder und alle weiteren Details aus, die Sie hinzufügen möchten.
Sobald Sie alle notwendigen Details eingegeben haben, klicken Sie auf die Schaltfläche "Publish", um Ihren neuen Beitrag zu speichern. Wiederholen Sie diesen Vorgang, um ein paar Beiträge für Ihren Blog zu erstellen.
Das Sanity-Schema deployen
Nachdem Sie Ihr Sanity Studio aktualisiert und Inhalte hinzugefügt haben, ist der nächste Schritt das Deployen des Schemas. Gehen Sie wie folgt vor:
Stellen Sie sicher, dass Sie mit dem Befehl login der Sanity CLI in Ihrem Sanity-Account eingeloggt sind. Wenn Sie dies noch nicht getan haben, finden Sie weitere Informationen hier.
Öffnen Sie Ihr Terminal und navigieren Sie zu Ihrem Projektordner (in diesem Fall "sanity-live-preview").
Führen Sie den folgenden Befehl aus, um das Schema zu deployen: npx sanity graphql deploy --no-playground
Der Befehl deploy wird verwendet, um die im Sanity Studio vorgenommenen Schema-Änderungen zu deployen.
Die Option --no-playground deaktiviert den GraphQL Playground, ein Tool zum Testen und Erkunden der GraphQL-API.
Warten Sie, bis der Deployment-Prozess abgeschlossen ist. Danach sollten Sie eine Erfolgsmeldung sehen.
Ihr Sanity-Schema ist jetzt deployt und bereit, in unserem Projekt verwendet zu werden.
Ein neues Gatsby-Projekt erstellen
Da wir nun den Inhalt in unserem Sanity Studio haben, erstellen wir ein neues Gatsby-Projekt, um diesen Inhalt anzuzeigen.
Gehen Sie folgendermaßen vor:
Öffnen Sie Ihr Terminal und navigieren Sie zum gewünschten Speicherort, an dem Sie das Gatsby-Projekt erstellen möchten.
Führen Sie den folgenden Befehl aus und folgen Sie den Eingabeaufforderungen, um ein neues Gatsby-Projekt zu initialisieren: npm init gatsby
Wenn Sie nach dem Website-Namen gefragt werden, geben Sie "Gatsby Live Preview" ein.
Lassen Sie den Ordnernamen als Standardwert.
Wählen Sie "JavaScript" als Sprache.
Wenn Sie gefragt werden, ob Sie ein CMS verwenden, wählen Sie "Sanity" und geben Sie Ihre Projekt-ID und Dataset-Informationen an. Diese Informationen finden Sie, indem Sie Ihr Projekt im Sanity Studio unter https://www.sanity.io/manage auswählen. Denken Sie daran, die relevanten API-Keys später in Umgebungsvariablen zu verschieben und sie nicht in Ihre Git-Historie einzuchecken.
Sie können die Projekt-ID und Dataset-Informationen auch später in der gatsby-config.js-Datei hinzufügen, falls Sie diesen Schritt übersprungen haben.
Wählen Sie "Tailwind CSS" als Styling-System.
Wählen Sie "Done", wenn Sie nach weiteren Features mit anderen Plugins gefragt werden.
Warten Sie, bis der Prozess abgeschlossen ist. Sobald er fertig ist, sehen Sie eine Erfolgsmeldung.
Wechseln Sie mit folgendem Befehl in das neu erstellte Verzeichnis: cd gatsby-live-preview
Um die Gatsby-App zu starten, führen Sie den folgenden Befehl aus: npx gatsby develop. Nach einigen Sekunden sollte Ihre Gatsby-App unter http://localhost:8000 verfügbar sein.
Seiten in Gatsby basierend auf Sanity-Beiträgen generieren
Sanity arbeitet ohne ein Konzept einer "Seite", da es vollständig agnostisch gegenüber der Art und Weise ist, wie Sie Ihren Inhalt präsentieren und über welche Plattform. Da wir jedoch Gatsby verwenden, müssen wir wahrscheinlich Seiten basierend auf unseren Inhalten erstellen! Gehen wir also die Schritte durch.
Erstellen Sie die Datei gatsby-node.js: Um zu beginnen, erstellen Sie eine Datei namens gatsby-node.js im Stammverzeichnis Ihres Gatsby-Projekts. Diese Datei enthält den notwendigen Code zum Generieren von Seiten basierend auf Ihren Sanity-Beiträgen.
Fügen Sie den notwendigen Code zu gatsby-node.js hinzu: Innerhalb der Datei gatsby-node.js müssen Sie den folgenden Code hinzufügen.
1exports.createPages = async ({ graphql, actions }) => {
2 const { createPage } = actions
3
4 const result = await graphql(`
5 {
6 allSanityPost(filter: { slug: { current: { ne: null } } }) {
7 edges {
8 node {
9 slug {
10 current
11 }
12 }
13 }
14 }
15 }
16 `)
17
18 // Handle any errors that occurred during the query
19 if (result.errors) {
20 throw result.errors
21 }
22
23 const posts = result.data.allSanityPost.edges || []
24
25 // Create a page for each Sanity post
26 posts.forEach((edge) => {
27 const path = `/${edge.node.slug.current}`
28
29 createPage({
30 path,
31 component: require.resolve("./src/templates/Post.jsx"),
32 context: {
33 slug: edge.node.slug.current,
34 },
35 })
36 })
37}In diesem Code verwenden wir die von Gatsby bereitgestellte createPages-Funktion, um Seiten basierend auf unseren Sanity-Beiträgen zu generieren. Wir fragen alle Sanity-Beiträge ab und verwenden dann die createPage-Aktion, um für jeden Beitrag eine Seite zu erstellen. Die Eigenschaft component gibt die Vorlagenkomponente an, die für das Rendern des einzelnen Beitrags verwendet wird, und die Eigenschaft context wird verwendet, um Daten an die Vorlagenkomponente zu übergeben, insbesondere den Slug des Beitrags.
Eine Beitragsvorlage erstellen
Um einen einzelnen Beitrag darzustellen und ihn mit Tailwind CSS zu gestalten, müssen Sie eine neue Datei namens Post.js im Verzeichnis src/templates erstellen. Diese Komponente definiert die Vorlage für das Rendern eines einzelnen Beitrags. Sie empfängt die Beitragsdaten als Props und rendert die notwendigen Elemente wie den Beiragstitel, den Textkörper und andere relevante Informationen. Hier ist der Link zur Gatsby-Dokumentation, die erklärt, wie man Seiten programmatisch erstellt: https://www.gatsbyjs.com/docs/programmatically-create-pages-from-data/#creating-pages
Kopieren Sie den folgenden Code in die neue Datei Post.js:
1import { Link, graphql } from "gatsby"
2import React from "react"
3import { PortableText } from "@portabletext/react"
4
5const Post = ({ data }) => {
6 const { title, _rawBody, mainImage } = data.sanityPost
7
8 const portableTextComponents = {
9 block: {
10 normal: ({ children }) => <p className="text-gray-700">
11 {children}
12 </p>,
13 h2: ({ children }) => <h2 className="my-4 text-xl font-light text-gray-800">
14 {children}
15 </h2>,
16 },
17 }
18
19 return (
20 <div className="min-h-screen p-6 bg-white">
21 <Link to="/" className="text-xs text-gray-700 underline">
22 Go back
23 </Link>
24 <div className="flex items-center w-full mt-6 mb-8">
25 <img src={mainImage?.asset?.url} className="h-80" objectFit="contain"/>
26 </div>
27 <h1 className="mb-3 text-2xl font-bold text-gray-900">{title}</h1>
28 <PortableText
29 value={_rawBody || []}
30 components={portableTextComponents}
31 />
32 </div>
33 )
34}
35
36export const query = graphql`
37 query ($slug: String!) {
38 sanityPost(slug: { current: { eq: $slug } }) {
39 title
40 _rawBody
41 mainImage {
42 asset {
43 url
44 }
45 }
46 publishedAt
47 }
48 }
49`
50export default PostIn diesem Code importieren wir die notwendigen Abhängigkeiten wie Link und graphql aus Gatsby, React für die Komponente und PortableText zum Rendern des Sanity-Inhalts (unter Verwendung des großartigen react-portabletext-Pakets). Die Post-Komponente empfängt die Beitragsdaten aus GraphQL und rendert Titel, Textkörper und Hauptbild des Beitrags.
Tailwind-Styling in unserer Vorlage aktivieren
Öffnen Sie Ihre tailwind.config.js-Datei und aktualisieren Sie das Content-Array, um das Verzeichnis src/templates einzuschließen:
Durch die Einbeziehung des Verzeichnisses src/templates in das Content-Array wird Tailwind CSS auf Dateien in diesem Verzeichnis angewendet.
1module.exports = {
2 content: [
3 `./src/pages/**/*.{js,jsx,ts,tsx}`,
4 `./src/components/**/*.{js,jsx,ts,tsx}`,
5 `./src/templates/**/*.{js,jsx,ts,tsx}`,
6 ],
7 theme: {
8 extend: {},
9 },
10 plugins: [],
11}Mit diesen Änderungen verfügen Sie nun über den notwendigen Code und die Styles, um einen einzelnen Beitrag mit der Post.js-Vorlagenkomponente in Gatsby anzuzeigen.
<GatsbyImage> für das Beitragsbild verwenden statt eines regulären <img>-Tags
Gatsby (insbesondere deren gatsby-plugin-image) enthält eine Komponente namens <GatsbyImage>, die mehrere Vorteile für die Optimierung von Bildladezeiten und -performance bietet:
Große Bilder verkleinern: Die <GatsbyImage>-Komponente verkleinert Bilder automatisch auf die von Ihrem Design benötigte Größe, reduziert die Dateigröße und verbessert die Ladezeiten.
Kleinere Bilder generieren: Mehrere kleinere Versionen des Bildes werden generiert, sodass Geräte wie Smartphones und Tablets keine Desktop-großen Bilder herunterladen müssen.
Unnötige Metadaten entfernen und Komprimierung optimieren: Die Komponente entfernt unnötige Metadaten und optimiert die JPEG- und PNG-Komprimierung, um die Dateigröße weiter zu reduzieren und die Ladegeschwindigkeit zu verbessern.
Effizientes Lazy Loading: Bilder werden lazy geladen, das bedeutet, sie werden nur geladen, wenn sie in den sichtbaren Bereich kommen, was die anfängliche Seitenladezeit verbessert und Bandbreite spart.
Vorschau-Techniken: Die Komponente kann Techniken wie "blur-up" oder einen "traced placeholder"-SVG verwenden, um eine Vorschau des Bildes während des Ladens anzuzeigen und so eine bessere User Experience zu bieten (Progressive Enhancement).
Bildposition beibehalten: Die Komponente hält die Bildposition aufrecht und verhindert, dass die Seite während des Ladens von Bildern springt.
Um <GatsbyImage> korrekt zu verwenden, müssen Sie die notwendigen Asset-Informationen abrufen, die an die Komponente übergeben werden sollen. Glücklicherweise stellt das Sanity-Source-Plugin eine Reihe von Hilfsfunktionen zur Verfügung, die das Abrufen dieser Informationen erleichtern.
Durch den Einsatz der <GatsbyImage>-Komponente können Sie die Performance und Ladegeschwindigkeit Ihrer Anwendung erheblich verbessern und gleichzeitig eine optimale User Experience gewährleisten.
Weitere Informationen zur Verwendung von <GatsbyImage> und zum Abrufen von Asset-Daten aus Sanity finden Sie in der Gatsby- und Source-Plugin-Dokumentation.
Fügen wir Folgendes in unsere Post.jsx-Komponente ein:
1import { Link, graphql } from "gatsby"
2import { GatsbyImage } from "gatsby-plugin-image"
3import React from "react"
4import { PortableText } from "@portabletext/react"
5
6const Post = ({ data }) => {
7 const { title, _rawBody, mainImage } = data.sanityPost
8
9 const portableTextComponents = {
10 block: {
11 normal: ({ children }) => <p className="text-gray-700">
12 {children}
13 </p>,
14 h2: ({ children }) => <h2 className="my-4 text-xl font-light text-gray-800">
15 {children}
16 </h2>,
17 },
18 }
19
20 return (
21 <div className="min-h-screen p-6 bg-white">
22 <Link to="/" className="text-xs text-gray-700 underline">
23 Go back
24 </Link>
25 <div className="flex items-center w-full mt-6 mb-8">
26 <GatsbyImage
27 image={mainImage?.asset?.gatsbyImageData}
28 className="h-80"
29 objectFit="contain"
30 />
31 </div>
32 <h1 className="mb-3 text-2xl font-bold text-gray-900">
33 {title}
34 </h1>
35 <PortableText
36 value={_rawBody || []}
37 components={portableTextComponents}
38 />
39 </div>
40 )
41}
42
43export const query = graphql`
44 query ($slug: String!) {
45 sanityPost(slug: { current: { eq: $slug } }) {
46 title
47 _rawBody
48 mainImage {
49 asset {
50 url
51 gatsbyImageData(placeholder: BLURRED)
52 }
53 }
54 publishedAt
55 }
56 }
57`
58
59export default PostIm obigen Code:
Die Post-Komponente empfängt das data-Prop, das das Ergebnis der GraphQL-Abfrage ist.
Die GraphQL-Abfrage wird mit export const query und dem graphql-Tag von Gatsby definiert. Sie ruft die Felder title, _rawBody, mainImage und publishedAt aus dem sanityPost-Objekt ab, wobei der Slug-Wert als Variable übergeben wird.
Das Feld mainImage enthält die url des Bild-Assets und verwendet gatsbyImageData, um die optimierten Bilddaten mit einem Platzhalter vom Typ BLURRED abzurufen.
Weitere Informationen zum Mechanismus finden Sie in der Gatsby-Dokumentation hier.
Wir zeigen jetzt unser Bild mit der <GatsbyImage>-Komponente an. Später in diesem Leitfaden werden wir zu dieser Komponente zurückkehren, da wir sie für Vorschauen anpassen müssen.
Die Startseite gestalten und alle Beiträge auflisten
Der nächste Schritt auf unserer To-do-Liste ist die Anpassung der Standard-index.jsx-Datei, die für die Startseite steht. Bereiten wir sie vor, indem wir etwas Styling hinzufügen, und fügen wir auch die erforderliche GraphQL-Logik hinzu, um alle verfügbaren Beiträge abzurufen.
Öffnen Sie die Datei index.jsx und fügen Sie den folgenden Code hinzu:
1import React from "react"
2import { Link, graphql } from "gatsby"
3
4const IndexPage = ({ data }) => {
5 const posts = data.allSanityPost.nodes
6
7 return (
8 <div className="flex min-h-screen bg-gray-100">
9 <div className="flex flex-col gap-6 p-8">
10 {posts.map((post) => (
11 <div
12 key={post.id}
13 className="flex flex-col gap-y-3 p-6 bg-white rounded-lg shadow-md"
14 >
15 <div className="flex gap-x-4 mb-3">
16 <img
17 className="object-cover mr-3 w-12 h-12 rounded-full shadow-inner"
18 src={post.mainImage.asset.url}
19 alt={post.title}
20 />
21 <div className="flex flex-col">
22 <h2 className="text-2xl font-semibold text-gray-900">
23 {post.title}
24 </h2>
25 <p className="text-sm text-gray-600">
26 {new Date(post.publishedAt).toLocaleDateString()}
27 </p>
28 </div>
29 </div>
30 <Link to={`${post.slug.current}`} className="text-sm text-gray-600">
31 Read more →
32 </Link>
33 </div>
34 ))}
35 </div>
36 </div>
37 )
38}
39
40export default IndexPage
41
42export const query = graphql`
43 query {
44 allSanityPost(sort: { fields: publishedAt, order: DESC }) {
45 nodes {
46 id
47 title
48 slug {
49 current
50 }
51 _rawBody
52 mainImage {
53 asset {
54 url
55 }
56 }
57 publishedAt
58 }
59 }
60 }
61`
62
63export const Head = () => <title>Home Page</title>Dieser Code passt die Startseite an, um alle verfügbaren Beiträge mit geeignetem Styling anzuzeigen, und ruft die Beitragsdaten über GraphQL-Abfragen ab.
Jetzt ist es Zeit, mit der Implementierung von Echtzeit-Vorschauen fortzufahren.
Was wir bisher erreicht haben
Wir haben erfolgreich eine funktionale Gatsby + Sanity-Seite erstellt, die es uns ermöglicht, in Sanity gespeicherte Beiträge abzurufen und anzuzeigen. Wir haben die Startseite gestaltet und alle verfügbaren Beiträge mit GraphQL-Abfragen aufgelistet.
In den nächsten Schritten werden wir Redakteuren ermöglichen, die Änderungen, die sie an diesem Inhalt vornehmen, in Echtzeit direkt innerhalb des Sanity Studios zu sehen. Glücklicherweise hat Sanity gerade ein leistungsstarkes Tool entwickelt, das uns dies einfach ermöglicht.
Vorschauen sowohl im Sanity Studio als auch im Gatsby-Frontend-Code implementieren
Zeit, mit der Implementierung der Vorschau-Funktionalität mit Sanity's preview-kit zu beginnen.
So wird die Lösung funktionieren:
Wir werden zunächst die integrierte Funktionalität von Sanity Studio durch benutzerdefinierte Views und Split-Panes erweitern. Diese sind dafür verantwortlich, Benutzern eine Echtzeit-Bearbeitungserfahrung zu bieten, da sie eine aktualisierte Version der Seite mit dem neuen Inhalt abrufen. Weitere Informationen zu benutzerdefinierten Views und Split-Panes finden Sie in diesem Sanity-Blogbeitrag.
Der benutzerdefinierte View rendert einen iFrame, der die Seite oder den Inhalt anzeigt, den der Benutzer gerade bearbeitet. Dieser benutzerdefinierte iFrame-View fügt auch die richtigen URL-Parameter an, damit wir sie später im Gatsby-Frontend-Code verwenden können.
Im Gatsby-Frontend-Code verwenden wir diese URL-Parameter, um Operationen wie das Einstellen des Vorschau-Modus und das Bereitstellen von Validierungsfehlern durchzuführen sowie um festzustellen, welches Dataset verwendet werden soll.
Während der Implementierung fügen wir die fehlenden Teile hinzu, die für den Konsum und das Rendern von Entwurfsdokumenten direkt mit Gatsby benötigt werden.
Außerdem behandeln wir Fälle wie das Reagieren auf Slug-Änderungen von Dokumenten oder den Umgang mit völlig neu erstellten Dokumenten im Vorschau-Modus.
Legen wir los!
Einen benutzerdefinierten View für Vorschauen im Sanity Studio erstellen
Kehren wir zu unserem sanity-live-preview Sanity Studio-Projekt zurück und erstellen wir einen benutzerdefinierten View für die Vorschau-Funktionalität.
Öffnen Sie die Datei sanity.config.ts in Ihrem Projekt.
Fügen Sie den structure-Schlüssel zur Plugin-Konfiguration von deskTool hinzu, um eine neue benutzerdefinierte Struktur für das deskTool zu definieren. Die aktualisierte sanity.config.ts-Datei sollte wie folgt aussehen:
1import { defineConfig } from 'sanity'
2import { deskTool } from 'sanity/desk'
3import { visionTool } from '@sanity/vision'
4import { schemaTypes } from './schemas'
5import deskStructure from './structure/deskStructure'
6
7export default defineConfig({
8 name: 'default',
9 title: 'Sanity Live Preview',
10 projectId: 'your-project-id',
11 dataset: 'production',
12 plugins: [deskTool({ structure: deskStructure }), visionTool()],
13 schema: {
14 types: schemaTypes,
15 },
16})deskStructure hinzufügen
Erstellen Sie einen neuen Ordner namens structure im Stammverzeichnis Ihres Studio-Projekts.
Erstellen Sie im Ordner structure eine neue Datei namens deskStructure.js.
Fügen Sie den folgenden Code zur Datei deskStructure.js hinzu:
1import { Views } from './views/preview'
2
3export default (S, context) => {
4 return S.list()
5 .title('Content')
6 .items(
7 S.listItem()
8 .title('Posts')
9 .child(
10 S.documentTypeList('post')
11 .title('Posts')
12 .child((id) => S.document().schemaType('post').documentId(id).views(Views(S, context)))
13 .defaultOrdering([{ field: 'title', direction: 'asc' }])
14 )
15 )
16}Der obige Code richtet die Struktur für das Sanity Studio deskTool-Plugin ein. Er erstellt ein Listenelement für den Abschnitt "Posts", in dem die Vorschau-Funktionalität verfügbar sein wird. Die Funktion Views ist für das Rendern des benutzerdefinierten iFrame-Views innerhalb von Sanity Studio verantwortlich.
Benutzerdefinierte Views definieren
Erstellen Sie im Ordner structure einen neuen Ordner namens views.
Erstellen Sie im Ordner views eine neue Datei namens preview.js.
Fügen Sie den folgenden Code zur Datei preview.js hinzu:
1import IframePreview from '../previews/IframePreview'
2
3const remoteURL = 'YOUR_DEPLOY_URL' // either production or a specialized deploy branch for previews
4const localURL = 'http://localhost:8000/' // local development port from Gatsby
5const previewUrl = window.location.hostname === 'localhost' ? localURL : remoteURL
6
7export const Views = (S, context) => {
8 return [
9 S.view.form(), // Default Sanity's form view for editing content
10 S.view
11 .component(IframePreview)
12 .options({ previewUrl, isMobilePreview: false, context })
13 .title('Preview'), // Iframe custom view for desktop screens
14 S.view
15 .component(IframePreview)
16 .options({ previewUrl, isMobilePreview: true, context })
17 .title('Mobile Preview'), // Iframe custom view for mobile screens
18 ]
19}Die <IframePreview>-Komponente
Die <IframePreview>-Komponente ist ein wichtiger Teil des Vorschau-Systems für Gatsby-Websites, die Sanity CMS verwenden. Durch den Einsatz eines iFrames erstellt die Komponente ein separates Fenster, in dem die Website-Vorschau angezeigt wird.
Gehen Sie folgendermaßen vor, um die <IframePreview>-Komponente zu implementieren:
Erstellen Sie im Ordner structure einen neuen Ordner namens previews.
Erstellen Sie im Ordner previews eine neue Datei namens IframePreview.jsx.
Fügen Sie den folgenden Code zur Datei IframePreview.jsx hinzu:
1import React, { useState, useEffect } from 'react'
2import { useValidationStatus } from 'sanity'
3
4// Function to assemble the preview URL based on the displayed object
5const assembleProjectUrl = ({ displayed, context, previewUrl, validation, isNewUnpublishedDoc }) => {
6 // Construct the base preview URL
7 const basePreviewUrl = previewUrl
8 const slug = displayed?.slug?.current
9 const validationArray = encodeURIComponent(JSON.stringify(validation))
10
11 // Check if slug or basePreviewUrl is missing
12 if (!slug || !basePreviewUrl) {
13 console.warn('Missing slug or previewURL', { slug, basePreviewUrl })
14 return ''
15 }
16
17 // Append the slug to the base URL to get the final page preview URL
18 const finalPagePreviewUrl = `${basePreviewUrl}${slug !== '/' ? slug : ''}`
19
20 return `${finalPagePreviewUrl}?previewMode=true&previewDataset=${context.dataset}&validation=${validationArray}&isNewUnpublishedDoc=${isNewUnpublishedDoc}`
21}
22
23// Component for rendering an iframe preview
24const IframePreview = ({ document, options }) => {
25 const [url, setUrl] = useState(null)
26 const { isMobilePreview, context, previewUrl } = options
27
28 // Get validation status
29 const { isValidating, validation } = useValidationStatus(
30 document?.published?._id || document?.displayed?._rev || document?.displayed?._id?.replace('drafts.', ''),
31 document?.published?._type || document?.displayed?._type
32 )
33
34 const isNewUnpublishedDoc = !document?.published?._id
35
36 // Update the URL when the document prop changes
37 useEffect(() => {
38 const { displayed } = document
39
40 setUrl(assembleProjectUrl({ displayed, context, previewUrl, validation, isNewUnpublishedDoc }))
41 }, [document, validation])
42
43 const { displayed } = document
44
45 // Render a message if there is no document to preview
46 if (!displayed) {
47 return (
48 <div
49 style={{
50 display: 'flex',
51 flexDirection: 'column',
52 justifyContent: 'center',
53 alignItems: 'center',
54 height: '100%',
55 width: '100%',
56 }}
57 >
58 <p>There is no document to preview</p>
59 </div>
60 )
61 }
62
63 // Render a message if there is a problem with the URL
64 if (!url) {
65 return (
66 <div
67 style={{
68 display: 'flex',
69 flexDirection: 'column',
70 justifyContent: 'center',
71 alignItems: 'center',
72 height: '100%',
73 width: '100%',
74 padding: '10px',
75 }}
76 >
77 <p>
78 There has been a problem constructing the web front-end URL. Please reselect the preview.
79 </p>
80 </div>
81 )
82 }
83
84 // Render the iframe preview
85 return (
86 <>
87 <div
88 style={{
89 width: '100%',
90 height: '100%',
91 display: 'flex',
92 flexDirection: 'column',
93 justifyContent: 'center',
94 alignItems: 'center',
95 }}
96 >
97 <div
98 style={{
99 width: '100%',
100 height: '100%',
101 maxWidth: isMobilePreview ? '360px' : 'none',
102 maxHeight: isMobilePreview ? '800px' : 'none',
103 transformOrigin: 'center',
104 transform: isMobilePreview ? 'scale(1.3)' : 'none', // Scale the iframe size if it's a mobile preview
105 }}
106 >
107 <iframe
108 title="Iframe-preview-noa"
109 src={url}
110 frameBorder="0"
111 allow="clipboard-write"
112 style={{ width: '100%', height: '100%' }}
113 />
114 </div>
115 </div>
116 </>
117 )
118}
119
120export default IframePreviewNachdem Sie die obigen Schritte befolgt haben, haben Sie einen benutzerdefinierten View für die Vorschau-Funktionalität im Sanity Studio erstellt. Sie sollten nun drei verfügbare Views für jeden Blog-Beitrag sehen: "editor", "preview" und "mobile preview".
Diese Views können als separate Panes verwendet werden, mit dem Formular auf der linken und der Vorschau auf der rechten Seite.
Die Echtzeit-Vorschau mit Sanity preview-kit implementieren
Der nächste Schritt ist das Abfragen und Anzeigen von Sanity-Entwurfsinhalten.
Zunächst müssen wir einige Pakete in unser Gatsby-Projekt installieren:
yarn add @sanity/client @sanity/preview-kit classnames
Stellen Sie sicher, dass Sie entweder Version @^4 oder @^6.4.2 und höher verwenden, da Versionen dazwischen aufgrund eines Webpack-Fehlers fehlschlagen (weitere Informationen finden Sie unter diesem Link).
Die installierten Pakete sind:
@sanity/client: Ein Paket zur Vereinfachung von Abfragen aus Sanity (und zum Senden von Mutationen).
@sanity/preview-kit: Eine Sammlung hilfreicher Funktionen, um Live-Vorschauen aus einer React-Anwendung heraus zu vereinfachen.
classnames: Eine Utility-Funktion, die wir später für bedingtes Styling verwenden werden.
Als Nächstes erstellen wir eine neue Datei, um Ihre Projektkonfiguration zu speichern und den Sanity-Client zu initialisieren. Erstellen Sie einen Ordner namens sanityUtils im Stammverzeichnis Ihres Gatsby-Projekts und erstellen Sie darin eine Datei namens sanity.js. Fügen Sie den folgenden Code zur Datei sanity.js hinzu:
1import { createClient } from "@sanity/client"
2
3const projectId = "your-project-id"
4const envDataset = "your-project-dataset"
5const apiVersion = "2023-07-01"
6
7export function getSanityPreviewClient(
8 preview,
9 previewDataset = envDataset
10) {
11 const client = createClient({
12 projectId,
13 dataset: previewDataset,
14 apiVersion,
15 useCdn: false, // to ensure data is fresh
16 token: preview.token,
17 })
18
19 return client
20}Ersetzen Sie im obigen Code "your-project-id" durch die tatsächliche Projekt-ID Ihres Sanity-Projekts und "your-project-dataset" durch das Dataset, das Sie in Ihrem Projekt verwenden. Bitte denken Sie daran, dies auch für andere Code-Beispiele in diesem Leitfaden zu tun.
Mit diesem Setup haben Sie die notwendigen Pakete installiert und den Sanity-Client für das Echtzeit-Abfragen und -Anzeigen von Entwurfsinhalten in Ihrem Gatsby-Projekt initialisiert.
Dokumente mit dem Sanity-Client abfragen
Um die Beiträge abzufragen, verwenden wir den Sanity-Client. Der Sanity-Client ruft die benötigten Daten mit GROQ ab, einer vom Sanity-Team entwickelten Abfragesprache. Wir können entsprechende GraphQL-Abfragen aus den GROQ-Äquivalenten erstellen, was durch die Fähigkeit von GROQ erleichtert wird, alle Attribute explizit mit dem ...-Operator zurückzugeben.
Das bedeutet, dass wir nicht jede einzelne GraphQL-Abfrage replizieren und mühsam jedes Feld angeben müssen, das wir verwenden möchten.
Wir müssen nur Felder angeben, bei denen wir Referenzen auflösen müssen (z. B. für Bilder) oder Unterschiede in der Datenstruktur zwischen Sanity und Gatsby's GraphQL-Schicht bestehen. Für dieses Beispiel haben wir jedoch alle Felder in die Abfrage eingeschlossen.
Eine hilfreiche GROQ-Kurzübersicht finden Sie hier.
Um zu beginnen, erstellen wir eine neue Datei namens documentQueries.js in einem neuen Ordner namens queries im Verzeichnis src. Diese Datei enthält alle Abfragen, die wir bei Bedarf wiederverwenden und erweitern können. Fügen Sie den folgenden Code zur Datei documentQueries.js hinzu:
1export const postQuery = `*[_type == "post" && slug.current == $slug][0] {
2 ...,
3 title,
4 body,
5 mainImage {
6 asset->,
7 },
8}`In dem obigen Code ist postQuery eine GROQ-Abfrage, die einen Beitrag basierend auf seinem Slug abruft. Diese Abfrage ruft den Titel, den Textkörper und das Hauptbild des Beitrags ab.
Bevor wir eine Testanfrage durchführen, um sicherzustellen, dass unser Setup korrekt funktioniert, müssen wir CORS-Zugriff im Sanity-Projekt-Dashboard aktivieren. Weitere Informationen zur Aktivierung von CORS finden Sie hier. Bitte beachten Sie auch die in diesem Dokument beschriebenen Sicherheitsimplikationen.
Gehen Sie dazu wie folgt vor:
Navigieren Sie zum API-Tab im Sanity-Projekt-Dashboard unter Manage | Sanity.io.
Geben Sie http://localhost:8000 und http://localhost:9000 für lokale Entwicklung und Produktions-Builds zum Testen ein und fügen Sie alle anderen URLs hinzu (z. B. dedizierte Preview-Deploy-Branch-URLs), die Sie für Vorschauen verwenden möchten.
Aktivieren Sie das Kontrollkästchen "Allow credentials".
Speichern Sie die Einstellungen.
Außerdem können Sie GROQ-Abfragen einfach im Sanity Vision Plugin testen.
Mit diesen abgeschlossenen Schritten haben wir nun die notwendige Abfrage eingerichtet und können mit dem Testen unseres Setups fortfahren.
Unsere erste Testanfrage durchführen
Lassen Sie uns nun unsere Post-Komponente modifizieren und einen useEffect für eine Testanfrage über den Sanity-Client hinzufügen.
1import { Link, graphql } from "gatsby"
2import { GatsbyImage } from "gatsby-plugin-image"
3import React, { useEffect } from "react"
4import { PortableText } from "@portabletext/react"
5import { getSanityPreviewClient } from "../../sanityUtils/sanity"
6import { postQuery } from "../queries/documentQueries"
7
8const Post = ({ data }) => {
9 const { title, _rawBody, mainImage, slug } = data.sanityPost
10
11 const portableTextComponents = {
12 block: {
13 normal: ({ children }) => <p className="text-gray-700">
14 {children}
15 </p>,
16 h2: ({ children }) => <h2 className="my-4 text-xl font-light text-gray-800">
17 {children}
18 </h2>,
19 },
20 }
21
22 useEffect(() => {
23 const fetchData = async () => {
24 console.log("slug:", slug.current)
25 const client = getSanityPreviewClient()
26 const post = await client.fetch(postQuery, { slug: slug.current })
27 console.log("post:", post)
28 }
29
30 fetchData()
31 }, [slug])
32
33 return (
34 <div className="min-h-screen p-6 bg-white">
35 <Link to="/" className="text-xs text-gray-700 underline">
36 Go back
37 </Link>
38
39 <div className="flex items-center w-full mt-6 mb-8">
40 <GatsbyImage
41 image={mainImage?.asset?.gatsbyImageData}
42 className="h-80"
43 objectFit="contain"
44 />
45 </div>
46
47 <h1 className="mb-3 text-2xl font-bold text-gray-900">
48 {title}
49 </h1>
50
51 <PortableText
52 value={_rawBody || []}
53 components={portableTextComponents}
54 />
55 </div>
56 )
57}
58
59export const query = graphql`
60 query($slug: String!) {
61 sanityPost(slug: { current: { eq: $slug } }) {
62 title
63 _rawBody
64 mainImage {
65 asset {
66 url
67 gatsbyImageData(placeholder: BLURRED)
68 }
69 }
70 publishedAt
71 }
72 }
73`
74
75export default PostIn der modifizierten <Post>-Komponente haben wir einen useEffect-Hook hinzugefügt, um eine Testanfrage über den Sanity-Client durchzuführen.
Mit diesen Änderungen sollten Sie beim Aufrufen der Beitragsseite die zugehörigen Beitragsinformationen in der Konsole protokolliert sehen. Dies kann dabei helfen zu überprüfen, ob das Setup korrekt funktioniert.
Einen PreviewContext für die Verwaltung des Vorschau-Zustands über mehrere Komponenten einrichten
Da wir bestätigt haben, dass der Sanity-Client nicht veröffentlichte Dokumente erfolgreich abrufen kann, können wir nun unsere Komponentenarchitektur erweitern, um den Zustandsaustausch über die gesamte Komponentenhierarchie hinweg zu ermöglichen und den Bedarf an übermäßigem Prop-Drilling zu vermeiden.
Dazu erstellen wir eine Datei namens PreviewContext.jsx, die für die Verwaltung des Anwendungszustands im Zusammenhang mit der Vorschau-Funktionalität verantwortlich ist.
Gehen Sie dazu folgendermaßen vor:
Öffnen Sie Ihr Projekt und finden Sie den Ordner src.
Im Ordner src erstellen Sie einen neuen Ordner namens context.
Im Ordner context erstellen Sie eine neue Datei namens PreviewContext.jsx.
In dieser Datei PreviewContext.jsx definieren wir den notwendigen Code, um den PreviewContext zu etablieren und zu verwalten.
1// PreviewContext.jsx
2
3import React, { createContext, useState } from "react"
4import PreviewIndicator from "./PreviewIndicator"
5
6// Set up the default context object
7const defaultContext = {
8 activePreview: "",
9 setActivePreview: "",
10 previewContextData: "",
11 setPreviewContextData: "",
12 previewIsLoading: "",
13 setPreviewIsLoading: "",
14}
15
16// Create the PreviewContext using createContext
17const PreviewContext = createContext(defaultContext)
18
19// Define the PreviewProvider component
20const PreviewProvider = ({ children }) => {
21 // Set up state variables using useState
22 const [activePreview, setActivePreview] = useState(false)
23 const [previewIsLoading, setPreviewIsLoading] = useState(false)
24 const [previewContextData, setPreviewContextData] = useState({
25 previewContextDataset: "production" // Adapt if you have another default dataset name
26 })
27 const [previewValidationData, setPreviewValidationData] = useState([])
28 const [isNewUnpublishedDoc, setIsNewUnpublishedDoc] = useState(false)
29
30 // Render the PreviewProvider component
31 return (
32 <PreviewContext.Provider
33 value={{
34 activePreview,
35 setActivePreview,
36 previewContextData,
37 setPreviewContextData,
38 previewIsLoading,
39 setPreviewIsLoading,
40 previewValidationData,
41 setPreviewValidationData,
42 isNewUnpublishedDoc,
43 setIsNewUnpublishedDoc
44 }}
45 >
46 {children}
47 {/* Render the PreviewIndicator component when activePreview is true */}
48 {activePreview && <PreviewIndicator isLoading={previewIsLoading} />}
49 </PreviewContext.Provider>
50 )
51}
52
53export { PreviewContext, PreviewProvider }Im obigen Code erstellen wir eine <PreviewContext>-Komponente mit der createContext-Funktion von React. Dieser Context ist für die Verwaltung des Anwendungszustands im Zusammenhang mit der Vorschau-Funktionalität verantwortlich.
Wir haben auch eine <PreviewProvider>-Komponente definiert, die die Anwendung umschließt und allen Komponenten in der Komponentenhierarchie die vorschau-bezogenen Zustandswerte bereitstellt. Innerhalb des PreviewProviders verwenden wir den useState-Hook, um den Anfangszustand für die Vorschau einzurichten.
Die Zustandsvariablen umfassen activePreview, um zu verfolgen, ob der Vorschau-Modus aktiv ist, previewIsLoading, um zu verfolgen, ob die Vorschau gerade geladen wird, previewContextData, um die Vorschau-Kontextdaten zu speichern (wie den Dataset-Namen), previewValidationData, um die Validierungsdaten für die Vorschau zu speichern, und isNewUnpublishedDoc, um zu verfolgen, ob das in der Vorschau angezeigte Dokument ein neues unveröffentlichtes Dokument ist.
Die <PreviewProvider>-Komponente rendert einen <PreviewContext.Provider> mit den genannten Kontextwerten. Sie rendert auch die <PreviewIndicator>-Komponente, wenn activePreview true ist, was anzeigt, dass der Vorschau-Modus aktiv ist. Dies ermöglicht ein konsistentes Rendering des Vorschau-Indikators über mehrere Komponenten hinweg.
Abschließend exportieren wir sowohl den PreviewContext als auch den PreviewProvider zur Verwendung in anderen Komponenten.
Jetzt erstellen wir im selben Ordner eine Komponente namens "PreviewIndicator", die für die Anzeige des Ladezustands des Entwurfsdokuments verantwortlich ist.
Erstellen Sie eine Datei namens PreviewIndicator.jsx und fügen Sie Folgendes hinzu:
1// PreviewIndicator.jsx
2
3import React from "react"
4import cx from "classnames"
5
6export default function PreviewIndicator({ isLoading = false }) {
7 return (
8 <div className="fixed inset-0 flex items-end justify-center w-screen h-screen pointer-events-none z-140">
9 <span className="inline-flex items-center gap-x-1.5 rounded-md px-2 py-1 text-xxxs sm:text-xs font-light text-gray-900 ring-1 ring-inset bg-white mb-6">
10 <svg
11 className={cx(
12 "w-2 h-2 bg-white transition-all duration-1000 animate",
13 {
14 "animate-pulse fill-yellow-500": isLoading,
15 "fill-green-500": !isLoading,
16 }
17 )}
18 viewBox="0 0 6 6"
19 aria-hidden="true"
20 >
21 <circle cx={3} cy={3} r={3} />
22 </svg>
23 {isLoading ? "Preview is loading" : "Preview up-to-date"}
24 </span>
25 </div>
26 )
27}Die <SanityPreviewConnector>-Komponente hinzufügen
Um die Rendering-Logik basierend auf dem aktiven Modus zu verwalten, erstellen wir eine Datei namens SanityPreviewConnector.jsx. Diese Komponente leitet die Children entweder weiter, wenn der Vorschau-Modus nicht aktiv ist, oder umschließt die Children im LiveQueryProvider, wenn der Vorschau-Modus aktiv ist. Sie ermöglicht auch das Client-seitige Abrufen von Entwurfsdokumenten basierend auf dem verfügbaren Zustand im PreviewContext.
Gehen Sie folgendermaßen vor, um die Komponente SanityPreviewConnector.jsx zu erstellen:
Erstellen Sie im Ordner components einen neuen Ordner namens preview.
Erstellen Sie im Ordner preview eine neue Datei namens SanityPreviewConnector.jsx.
Fügen Sie den folgenden Code zur Datei SanityPreviewConnector.jsx hinzu:
1import { LiveQueryProvider } from "@sanity/preview-kit"
2import React, { useContext } from "react"
3import { getSanityPreviewClient } from "../../../sanityUtils/sanity"
4import { PreviewContext } from "../../context/PreviewContext"
5
6export default function SanityPreviewConnectorProvider({ children, token }) {
7 const { activePreview, previewContextData } = useContext(PreviewContext)
8 const client = getSanityPreviewClient(
9 { token },
10 previewContextData?.previewDataset
11 )
12
13 if (!activePreview) {
14 // Return the regular children with no draft documents
15 return <>{children}</>
16 }
17
18 // Preview mode enabled
19 return (
20 <LiveQueryProvider client={client}>
21 {children}
22 </LiveQueryProvider>
23 )
24}Wenn der Vorschau-Modus nicht aktiv ist, rendert die Komponente einfach die Children ohne Entwurfsdokumente.
Wenn der Vorschau-Modus aktiv ist, umschließt die Komponente die Children im LiveQueryProvider und übergibt den von getSanityPreviewClient erhaltenen Client. Dies ermöglicht das Abrufen von Entwurfsdokumenten im Vorschau-Modus.
Schließlich exportieren wir die <SanityPreviewConnectorProvider>-Komponente zur Verwendung in anderen Teilen der Anwendung.
Die Verbindungen mit gatsby-browser.js herstellen
Nachdem wir unseren Context und Vorschau-Connector für den globalen Zugriff in unserer Anwendung vorbereitet haben, können wir mit dem letzten Schritt der Strukturierung unserer Komponentenhierarchie und der letzten Aspekte der Integration fortfahren.
Um die notwendigen Verbindungen herzustellen, gehen Sie folgendermaßen vor:
Öffnen Sie Ihre Datei gatsby-browser.js.
Fügen Sie den folgenden Code zur Datei hinzu:
1import React from "react"
2import "./src/styles/global.css"
3import SanityPreviewConnectorProvider from "./src/components/Preview/SanityPreviewConnector"
4import { PreviewProvider } from "./src/context/PreviewContext"
5
6export const wrapRootElement = ({ element }) => {
7 return (
8 <PreviewProvider>
9 <SanityPreviewConnectorProvider token={process.env.YOUR_SECRET_TOKEN}>
10 {element}
11 </SanityPreviewConnectorProvider>
12 </PreviewProvider>
13 )
14}Wir verwenden die von Gatsby bereitgestellte wrapRootElement-Funktion, um unsere gesamte Anwendung mit den notwendigen Providern zu umschließen.
PreviewProvider ist dafür verantwortlich, den für die Vorschau-Funktionalität benötigten Context bereitzustellen.
Als Nächstes wird SanityPreviewConnectorProvider verwendet, um zu bestimmen, welcher Modus (Vorschau oder Live) aktiv sein soll und die Rendering-Logik entsprechend zu verwalten. Es empfängt das Token-Prop, das Ihr geheimes Token sein sollte. Es ist wichtig, Umgebungsvariablen für dieses Token zu verwenden, um die Sicherheit zu gewährleisten. Weitere Informationen zur Generierung Ihres geheimen Schlüssels finden Sie in der Sanity-Dokumentation.
Schließlich rendern wir das Element innerhalb der verschachtelten Provider.
Bitte stellen Sie sicher, dass Sie im Abschnitt "Tokens" von Sanity.io ein Viewer-Token (ohne Schreibzugriff) hinzugefügt haben. Gehen Sie beim Umgang mit solchen Tokens vorsichtig vor, da sie besondere Berechtigungen gewähren.
Mit diesen Verbindungen haben wir nun das notwendige Fundament für die Integration der Sanity-Vorschau-Funktionalität in unsere Gatsby-Anwendung gelegt.
Ihren Sanity-Token schützen
Es ist wichtig, Ihr privates Sanity-Token zu schützen, um die Sicherheit Ihres Content-Management-Systems zu gewährleisten, Ihre Geschäftsinformationen zu schützen und sicherzustellen, dass nur autorisierte Benutzer Zugang zu Ihren Daten haben.
Um Ihr Token zu schützen, halten Sie es vertraulich und teilen Sie es niemals mit anderen.
Vermeiden Sie außerdem das Speichern des Tokens in der Codebasis, da es Teil des Anwendungsbundles werden und öffentlich zugänglich sein könnte.
Weitere Informationen finden Sie unter:
Ihr Setup absichern
Um sicherzustellen, dass nur autorisierte Benutzer auf die Vorschau-Daten zugreifen können, verwenden wir die Methode client.config({withCredentials: true}). Diese Methode ermöglicht es uns, den Login-Status des Benutzers innerhalb der Studio-Umgebung zu authentifizieren und zu validieren.
Durch den Einsatz von client.config({withCredentials: true}) können wir bestätigen, dass der Benutzer sich bereits im Studio eingeloggt hat, bevor Vorschau-Daten angezeigt werden. Bei dieser Methode ist kein Token erforderlich, und wir fügen eine zusätzliche Sicherheitsebene hinzu, die sicherstellt, dass nur authentifizierte Benutzer auf den Vorschau-Inhalt zugreifen können, ohne das Vorschau-Token Client-seitig preiszugeben.
Schauen wir uns die Implementierung an:
Gehen Sie zu Ihrer sanity.ts-Datei und entfernen Sie das Token-Attribut in der createClient()-Funktion von getSanityPreviewClient, da dieser Ansatz kein Token benötigt.
Öffnen Sie dann Ihre Datei SanityPreviewConnector.jsx und fügen Sie den folgenden useEffect hinzu:
1import { LiveQueryProvider } from "@sanity/preview-kit"
2import React, { useContext, useEffect } from "react"
3import {
4 getSanityClient,
5 getSanityPreviewClient,
6} from "../../../sanityUtils/sanity"
7import { PreviewContext } from "../../context/PreviewContext"
8
9export default function SanityPreviewConnectorProvider({ children, token }) {
10 const { activePreview, previewContextData } = useContext(PreviewContext)
11
12 useEffect(() => {
13 async function getSanityUserData() {
14 const response = await getSanityClient()
15 .config({ withCredentials: true })
16 .users.getById("me")
17 }
18 getSanityUserData()
19 }, [])
20
21 if (!activePreview) {
22 return <>{children}</> // Return regular children without draft documents
23 }
24
25 const client = getSanityPreviewClient(previewContextData?.previewDataset)
26
27 return <LiveQueryProvider client={client}>{children}</LiveQueryProvider> // Preview mode enabled
28}Die Funktion getSanityUserData() ruft die Benutzerdaten von Sanity mithilfe der im Browser gespeicherten Cookies ab. Diese Daten enthalten Informationen über den eingeloggten Benutzer, wie seinen Benutzernamen, seine E-Mail-Adresse und andere relevante Details. Sobald die Daten des Benutzers abgerufen wurden, werden alle zukünftigen Anwendungsanfragen im Namen dieses eingeloggten Benutzers durchgeführt. Diese Funktion stellt sicher, dass der Benutzer bei Sanity authentifiziert ist und die notwendigen Cookies in seinem Browser gespeichert hat. Wenn der Benutzer nicht eingeloggt ist oder die Cookies nicht verfügbar sind, kann er nicht auf den Vorschau-Inhalt zugreifen.
Entwurfsinhalte für Beiträge abrufen
Jetzt, da alles eingerichtet ist, können wir die Entwurfsinformationen in unserer Post-Komponente abrufen. Bevor wir mit der Implementierung fortfahren, gibt es jedoch einige Aspekte zu berücksichtigen:
Die <GatsbyImage>-Komponente: Die <GatsbyImage>-Komponente erfordert ein Objekt mit Asset-Informationen in einer bestimmten Struktur. Da wir nun Informationen direkt von Sanity über den Sanity-Client abrufen und nicht mehr das Plugin gatsby-source-sanity verwenden, ist die Datenstruktur leicht unterschiedlich. Um die erforderliche Struktur zu erstellen, können Sie die Funktion getGatsbyImageData verwenden, die vom Plugin gatsby-source-sanity bereitgestellt wird. Die Post-Komponente wird alle notwendigen Informationen haben, um dies zu ermöglichen. Eine Referenz zur Funktion finden Sie hier.
_raw-Felder: Beim direkten Abrufen von Informationen aus Sanity sind keine _raw-Felder verfügbar. Daher müssen wir unsere Post-Komponente anpassen, um je nach aktivem Modus entweder _rawBody oder body zu verwenden. Die Post-Komponente sollte diese Anpassungen verwalten, um sicherzustellen, dass unsere <PortableText>-Komponente in beiden Szenarien funktioniert. Eine Referenz zu raw-Feldern finden Sie hier.
Es könnte eine gute Idee sein, diese Unterschiede in einer Wrapper-Komponente zu behandeln, um die bedingte Logik nicht in der gesamten Codebasis zu wiederholen.
Nachdem wir die wesentlichen Unterschiede bei der Implementierung der <GatsbyImage>-Komponente oder anderer von _raw-Feldern abhängiger Komponenten erläutert haben, fügen wir den Code hinzu, der zum Abrufen unseres Entwurfsbeitrags benötigt wird.
1import { Link, graphql } from "gatsby"
2import React, { useEffect, useContext } from "react"
3import { PortableText } from "@portabletext/react"
4import { postQuery } from "../queries/documentQueries"
5import { PreviewContext } from "../context/PreviewContext"
6import { useLiveQuery } from "@sanity/preview-kit"
7import { InformationCircleIcon } from "@heroicons/react/24/solid"
8import { GatsbyImage } from "gatsby-plugin-image"
9import { getGatsbyImageData } from "gatsby-source-sanity"
10
11const Post = ({ data }) => {
12 const [previewData, sanityPreviewIsLoading] = useLiveQuery(null, postQuery, {
13 slug: data.sanityPost.slug.current,
14 })
15
16 const {
17 setActivePreview,
18 setPreviewContextData,
19 setPreviewIsLoading,
20 setPreviewValidationData,
21 setIsNewUnpublishedDoc,
22 isNewUnpublishedDoc,
23 } = useContext(PreviewContext)
24
25 useEffect(() => {
26 setPreviewIsLoading(sanityPreviewIsLoading)
27 }, [sanityPreviewIsLoading])
28
29 const sanityConfig = { projectId: "YOUR_PROJECT_ID", dataset: "production" }
30
31 const portableTextComponents = {
32 block: {
33 normal: ({ children }) => <p className="text-gray-700">
34 {children}
35 </p>,
36 h2: ({ children }) => <h2 className="my-4 text-xl font-light text-gray-800">
37 {children}
38 </h2>,
39 },
40 }
41 useEffect(() => {
42 // Get URL params
43 const urlSearchParams = new URLSearchParams(window.location.search)
44 const previewModeParameter = urlSearchParams.get("previewMode")
45 const previewDatasetParameter = urlSearchParams.get("previewDataset")
46 const previewValidationDataParameter = urlSearchParams.get("validation")
47 const previewIsNewUnpublishedDocParameter =
48 urlSearchParams.get("isNewUnpublishedDoc") === "true"
49
50 if (previewValidationDataParameter) {
51 setPreviewValidationData(JSON.parse(previewValidationDataParameter))
52 }
53
54 if (previewModeParameter) {
55 setActivePreview(true)
56 }
57 if (previewDatasetParameter) {
58 setPreviewContextData({ previewDataset: previewDatasetParameter })
59 }
60
61 if (previewIsNewUnpublishedDocParameter) {
62 setIsNewUnpublishedDoc(previewIsNewUnpublishedDocParameter)
63 }
64 }, [])
65
66 // Show a Loading message
67 if (sanityPreviewIsLoading) {
68 return <div>Loading...</div>
69 }
70
71 // Non published document message
72 if (!sanityPreviewIsLoading && isNewUnpublishedDoc) {
73 return (
74 <div className="p-4 mt-12 rounded-md bg-yellow-50">
75 <div className="flex">
76 <div className="flex-shrink-0">
77 <InformationCircleIcon
78 className="w-5 h-5 text-yellow-700"
79 aria-hidden="true"
80 />
81 </div>
82 <div className="ml-3">
83 <h3 className="mb-2 font-medium text-yellow-700 text-xxxxs">
84 <b>Preview mode info</b>
85 </h3>
86 <h3 className="font-medium leading-snug text-yellow-700 text-xxxxs">
87 You have created a new document that isn't yet published. Please
88 publish to access preview mode.
89 </h3>
90 </div>
91 </div>
92 </div>
93 )
94 }
95
96 return (
97 <div className="min-h-screen p-6 bg-white">
98 <Link to="/" className="text-xs text-gray-700 underline">
99 Go back
100 </Link>
101 <div className="flex items-center w-full mt-6 mb-8">
102 <GatsbyImage
103 image={
104 getGatsbyImageData(
105 previewData?.mainImage?.asset?._id,
106 {},
107 sanityConfig
108 ) || data.sanityPost.mainImage?.asset?.gatsbyImageData
109 }
110 className="h-80"
111 objectFit="contain"
112 alt={"post cover"}
113 />
114 </div>
115 <h1 className="mb-3 text-2xl font-bold text-gray-900">
116 {previewData?.title || data.sanityPost.title}
117 </h1>
118 <PortableText
119 value={previewData?.body || data.sanityPost._rawBody || []}
120 components={portableTextComponents}
121 />
122 </div>
123 )
124}
125
126export const query = graphql`
127 query ($slug: String!) {
128 sanityPost(slug: { current: { eq: $slug } }) {
129 title
130 _rawBody
131 mainImage {
132 asset {
133 gatsbyImageData(placeholder: BLURRED)
134 url
135 }
136 }
137 slug {
138 current
139 }
140 publishedAt
141 }
142 }
143`
144
145export default Post
Live-Vorschauen sind jetzt in Ihrem Gatsby-Projekt implementiert!
Neu erstellte Dokumente und Slug-Änderungen für Entwurfsdokumente über die 404-Seite verwalten
Wenn Benutzer Seiten oder Beiträge erstellen, die noch nicht existieren, oder den Dokument-Slug ändern, kann dies zu einem 404-Fehler führen, da diese Seite von Gatsby noch nicht gebaut wurde. Um diesen Fall zu behandeln, können wir eine entsprechende Behandlung in den 404-Mechanismus einführen.
Fügen Sie in der 404-Datei, die Gatsby während der Projektinitialisierung für Sie erstellt hat, den folgenden Code hinzu:
1import React, { useEffect, useContext } from "react"
2import { Link } from "gatsby"
3import { PreviewContext } from "../context/PreviewContext"
4import Post from "../templates/Post"
5
6const pageStyles = {
7 color: "#232129",
8 padding: "96px",
9 fontFamily: "-apple-system, Roboto, sans-serif, serif",
10}
11
12const headingStyles = {
13 marginTop: 0,
14 marginBottom: 64,
15 maxWidth: 320,
16}
17
18const paragraphStyles = {
19 marginBottom: 48,
20}
21
22const codeStyles = {
23 color: "#8A6534",
24 padding: 4,
25 backgroundColor: "#FFF4DB",
26 fontSize: "1.25rem",
27 borderRadius: 4,
28}
29
30const NotFoundPage = () => {
31 const {
32 activePreview,
33 setActivePreview,
34 setPreviewContextData,
35 setPreviewValidationData,
36 } = useContext(PreviewContext)
37
38 const getSlug = (url: string) => new URL(url).pathname.match(/[^\/]+/g)
39
40 useEffect(() => {
41 const urlSearchParams = new URLSearchParams(location.search)
42 const previewModeParameter = urlSearchParams.get("previewMode")
43 const previewDatasetParameter = urlSearchParams.get("previewDataset")
44 const previewValidationDataParameter = urlSearchParams.get("validation")
45
46 if (previewValidationDataParameter) {
47 setPreviewValidationData(JSON.parse(previewValidationDataParameter))
48 }
49
50 if (previewModeParameter) {
51 setActivePreview(true)
52 }
53
54 if (previewDatasetParameter) {
55 setPreviewContextData({ previewDataset: previewDatasetParameter })
56 }
57 }, [])
58
59 if (activePreview) {
60 const pageData = {
61 sanityPost: {
62 slug: {
63 current: getSlug(window.location.href)?.join("/"),
64 },
65 },
66 }
67
68 return <Post data={pageData}></Post>
69 }
70
71 return (
72 <main style={pageStyles}>
73 <h1 style={headingStyles}>Page not found</h1>
74 <p style={paragraphStyles}>
75 Sorry, we couldn't find what you were looking for.
76 <br />
77 {process.env.NODE_ENV === "development" ? (
78 <>
79 <br />
80 Try creating a page in
81 <code style={codeStyles}>
82 src/pages/
83 </code>.
84 <br />
85 </>
86 ) : null}
87 <br />
88 <Link to="/">Go home</Link>.
89 </p>
90 </main>
91 )
92}
93
94export default NotFoundPageDer obige Code behandelt den Fall neu erstellter Dokumente oder Slug-Änderungen, indem er den neuen Slug aus der URL abruft, die von unserer <IframePreview>-Komponente im Sanity Studio gesendet wird. Er übergibt dann diesen Slug an unsere <Post>-Komponente, die den Slug verwendet, um die entsprechenden Beitragsdaten mit dem useLiveQuery-Hook abzurufen. Fügen Sie weitere Seitentypen nach Bedarf hinzu und Sie sind startklar!
Auf Netlify deployen
Mit der abgeschlossenen Einrichtung von Sanity's Preview-Kit deployen wir unsere Ergebnisse auf Netlify, indem wir folgenden Schritten folgen.
Loggen Sie sich in Ihr Netlify-Dashboard ein und klicken Sie auf "Add new site" und dann auf "Import an existing project".
Wählen Sie Ihren Git-Anbieter und Ihr Repository.
Netlify erkennt es als Gatsby-Projekt. Behalten Sie die Standardeinstellungen bei und klicken Sie auf "Deploy".
Sobald Ihre Gatsby-Website auf Netlify live ist und Sie Ihre Deploy-URL zur Sanity Studio-Umgebungsvariable hinzugefügt haben, kann Ihr Team von der neu erstellten Echtzeit-Vorschau-Erfahrung profitieren!
Fazit
In diesem Leitfaden haben wir die Implementierung eines leistungsstarken Vorschau-Systems für Gatsby-Websites mit Sanity CMS abgeschlossen. Dieses Vorschau-System ermöglicht es Content-Redakteuren, eine Echtzeit-Vorschau ihrer Änderungen vor der Veröffentlichung zu sehen, was den Bedarf an einer Build-Pipeline eliminiert und die Reibung im Content-Creation-Prozess reduziert.
Im Vergleich zu anderen Vorschau-Setups, die auf Build-Pipelines angewiesen waren, bietet dieser Ansatz erhebliche Verbesserungen. Er eliminiert die Notwendigkeit, die gesamte Website für jede Inhaltsänderung neu zu bauen, was zu schnelleren Iterationszyklen führt und die Zeit verkürzt, die für die Vorschau benötigt wird.
Wenn Ihnen dieser Leitfaden hilfreich war, schauen Sie sich gerne meine anderen Leitfäden an, wie z. B. wie man ein Farb-Design-System für Redakteure mit Sanity erstellt oder unsere Fallstudie über Monitoring und automatisiertes Testing für eine große kanadische E-Commerce-Marke.
Wenn Sie Unterstützung bei der Implementierung eines ähnlichen Vorschau-Systems (oder dessen Erweiterung) oder anderer benutzerdefinierter Systeme für Ihre Plattform benötigen, zögern Sie nicht, mich zu kontaktieren. Ich bin hier, um Sie zu unterstützen und maßgeschneiderte Lösungen zu entwickeln, die Ihren spezifischen Anforderungen entsprechen.