Erkundung der Optimizely GraphQL-API zur Bereitstellung einer leistungsstarken statischen Website – Teil 1.

In dieser Artikelserie werde ich demonstrieren, wie man eine Website mit Optimizely, Next.js und Vercel ausliefert. In diesem ersten Teil konzentriere ich mich auf die Erstellung eines einfachen POC, in dem ich eine vereinfachte Version der AlloyTech-Website reproduziere, die jedoch mit Next.js und Vercel statisch generiert wird.

Lösungsarchitektur

Optimizely wird verwendet, um den Inhalt zu verwalten, wobei alle Inhaltsaktualisierungen mit dem Inhaltsdiagramm synchronisiert werden.

Die Präsentationsschicht wird mit Next.js, einem React-Framework, entwickelt. Next.js kann entweder zur Erstellungszeit eine statische Website generieren, das serverseitige Rendering verarbeiten oder eine Kombination aus beidem.

Vercel ist ein globales Netzwerk, das Hosting anbietet. Der Inhalt wird weltweit zwischengespeichert.

Schritt 1 – Verwalten des Inhalts

Der erste Schritt ist einfach und nach Abschluss erhalten Sie eine Instanz von AlloyTech, deren Inhalt mit dem Inhaltsdiagramm synchronisiert ist.

Installieren Sie AlloyTech

Installieren, erstellen und führen Sie die Demo-Site mit den folgenden Befehlen aus. Wenn Sie die Lösung zum ersten Mal ausführen, werden Sie aufgefordert, ein Administratorkonto zu erstellen. Sobald dies abgeschlossen ist, haben Sie eine Website, die Sie zum Testen verwenden können.

dotnet new epi-alloy-mvc
dotnet build
dotnet run

Content Graph installieren

Führen Sie die folgenden Schritte aus, um Content Graph auf der AlloyTech-Demo-Site hinzuzufügen und zu konfigurieren. Sie müssen sich an Optimizely wenden, um Zugriff auf einen AppKey zu erhalten.

dotnet add package Optimizely.ContentGraph.Cms
  "Optimizely": {
    "ContentGraph": {
      "GatewayAddress": "https://cg.optimizely.com",
      "AppKey": "",
      "Secret": "",
      "SingleKey": "",
      "AllowSendingLog": "true"
    }
  }

Nach der Installation und Konfiguration können Sie Ihre Inhalte mithilfe des geplanten Jobs „Content Graph Content Synchronization Job“ mit dem Content Graph synchronisieren. Inhalte werden auch synchronisiert, wenn sie veröffentlicht werden.

Schritt 2 – Entwickeln Sie die Website mit Next.js

Das Erstellen einer neuen Website mit Next.js ist sehr einfach, aber werfen Sie einen Blick auf https://nextjs.org/docs/getting-started für detailliertere Anweisungen.

Sie können nun auf die Site zugreifen, indem Sie in Ihrem Browser „http://localhost:3000/“ eingeben.

Next.js enthält keine GraphQL-Bibliotheken, ich habe dafür das ApolloClient-Paket hinzugefügt.

npm install @apollo/client graphql

Neuerstellung der Homepage

Ich möchte eine vereinfachte Version der AlloyTech-Homepage erstellen. Ich werde die Primärnavigation zusammen mit den Blöcken aus dem Hauptinhaltsbereich rendern, dies ist analog zur Vorgehensweise in der C#-Version der Seite.

Die Seite hat keine Kenntnis darüber, woher die Daten stammen, diese werden über einen weiteren Schritt übergeben. Es verwendet nur das Objekt, das in den ‘Props’ gesendet wird.

type PageProps = {
  page: any;
  navigation: any;
};

function Home(props: PageProps) {
  const { page, navigation } = props;
  return (
    <>
      
        {page.MetaTitle}
        
        
        
      

      
      
      
) }

Jede Next.js-Seite kann ein „getStaticProps‘-Funktion, die während des Erstellungsprozesses verwendet wird, um die beim Rendern verwendeten Requisiten zurückzugeben. Hier fragen wir den Content Graph ab, um die Daten für die Startseite (und Navigation) zu erhalten.

Lesen Sie auch  Chrishell Stause enthüllt den Beauty-Hack, der sie jung aussehen lässt

Notiz: ‘getStaticProps’ wird nur für die statische Site-Generierung verwendet, für das serverseitige Rendering wird eine andere Methode aufgerufen.

export const getStaticProps: GetStaticProps = async (context) => {

  const httpLink = new HttpLink({ uri: process.env.GRAPHQL_HOST });

  const client = new ApolloClient({
    link: httpLink,
    cache: new InMemoryCache(),
    ssrMode: true
  });
 
  var { data } = await client.query({
    query: StartPageQuery
  })

  var startPage = data.StartPage.items[0];

  var { data } = await client.query({
    query: NavigationQuery
  })
  
  var navigation = data.StartPage.items[0];

  console.log(navigation)

  return {
    props: {
      page: startPage,
      navigation: navigation
    },
  }
}

GraphQL-Abfrage zum Abrufen der Startseite.

import { gql } from '@apollo/client';

const StartPageQuery = gql`
query MyQuery {
  StartPage(locale: en) {
    items {
      Name
      TeaserText
      RouteSegment
      MetaTitle
      MetaKeywords
      MetaDescription
      MainContentArea {
        DisplayOption
        Tag
        ContentLink {
          Id
          Expanded {
            Name
            ContentType
            ... on JumbotronBlock {
              Name
              Heading
              Image {
                Url
              }
              ButtonText
              ContentType
              SubHeading
            }
            ... on TeaserBlock {
              _score
              Name
              Image {
                Url
              }
              Heading
              Text
            }
          }
        }
      }
    }
  }
}`
export default StartPageQuery

Produktseiten

Die Startseite ist ein einfaches Beispiel, aber was passiert, wenn Sie viele Inhalte haben, die dieselbe Vorlage verwenden? In AlloyTech gibt es 3 Produktseiten, auf die als untergeordnete Seiten der Homepage zugegriffen wird.

Routing

Die Namenskonvention von ‘[product-slug].tsx‘ bedeutet, dass die Seite eine dynamische Route ist. Der Name in den eckigen Klammern ‘[]‘ ist nicht wichtig.

Next.js geht hier näher darauf ein: https://nextjs.org/docs/routing/introduction.

Generieren der Routen

Ähnlich wie die ‘getStaticProps‘ Funktion, Next.js hat einen Ansatz zum Generieren der Routen, ‘getStaticPaths‘. Dies wird auch zur Build-Zeit aufgerufen.

export const getStaticPaths: GetStaticPaths = async () => {
    const httpLink = new HttpLink({ uri: process.env.GRAPHQL_HOST });

    const client = new ApolloClient({
      link: httpLink,
      cache: new InMemoryCache(),
      ssrMode: true
    });
   
    var { data } = await client.query({
      query: gql`query ProductPagesQuery {
        ProductPage(locale: en) {
          items {
            Name
            RouteSegment
          }
        }
      }`
    })
    var pages = data.ProductPage.items;

    const paths = pages.map((page: any) => ({
      params: { slug: page.RouteSegment}, locale: 'en',
    }));
  
    return { paths, fallback: false };
  };

Generieren der Seite

‘getStaticPaths’ für den Aufbau aller Routen verantwortlich ist, jede Route wird dann verwendet, um eine einzelne Seite zu generieren, wobei die Routendaten an ‘getStaticProps‘.

export const getStaticProps: GetStaticProps = async ({params}) => {

  if (!params || !params.slug) {
    return { props: {} };
  }

  const httpLink = new HttpLink({ uri: process.env.GRAPHQL_HOST });

  const client = new ApolloClient({
    link: httpLink,
    cache: new InMemoryCache(),
    ssrMode: true
  });
 
  var { data } = await client.query({
    query: ProductPageQuery,
    variables: {
      segment: params.slug
    }
  })

  var page = data.ProductPage.items[0];

  var { data } = await client.query({
    query: NavigationQuery
  })
  
  var navigation = data.StartPage.items[0];
  return {
    props: {
      page: page,
      navigation: navigation
    },
  }
}

Die folgende GraphQL-Abfrage ruft die spezifische Seite ab, die der Route entspricht

import { gql } from '@apollo/client';

const ProductPageQuery = gql`
query ProductPageQuery($segment: String) {
  ProductPage(locale: en, where: {RouteSegment: {eq: $segment}}) {
    items {
      Name
      MetaTitle
      MetaKeywords
      MetaDescription
      MainBody
      TeaserText
      RelativePath
      PageImage {
        Url
      }
      RouteSegment
    }
  }
}
`
export default ProductPageQuery

Inhaltsbereiche / Blöcke

Für diesen POC habe ich meinen eigenen Content Area Render erstellt, da dies ein Optimizely-Konzept ist, das eine benutzerdefinierte Entwicklung innerhalb Ihrer Next.js-Site erfordert.

Lesen Sie auch  - Ich fühlte mich noch nicht fertig

Der Ansatz ist sehr einfach, das Rendern des Inhaltsbereichs iteriert über jedes Element und verwendet eine Factory, um die zu rendernde Komponente zu bestimmen. Diese Fabrik erhält auch die Anzeigeoption, mit der die Blöcke in verschiedenen Größen gerendert werden können.

function ContentAreaRenderer(props :any) {

    let items :any[] = props.items;

    var factory = new componentFactory()

    return(
        
{items?.map(i => { const ContentAreaItem = factory.resolve(i); const Component = ContentAreaItem.Component; if (Component != null) return (
) else return null })}
) }

Der ‘KomponentenFabrik‘ erhält die richtige Komponente zum Rendern und auch die richtige Anzeigeoption.

class ContentAreaItem {
    ItemClasses: string;
    Component: any;

    constructor () {
        this.ItemClasses = "fullwidth"
    }
}
interface Dictionary {
    [Key: string]: T;
}

class componentFactory {
  
    components: Dictionary = {};

    constructor(){
        this.components["JumbotronBlock"] = JumbotronBlock;
        this.components["TeaserBlock"] = TeaserBlock;
    } 

    getType(item: any) : string {
        var contentTypes = item.ContentLink.Expanded.ContentType;
        return contentTypes[contentTypes.length - 1]; 
    }

    getDisplayOption(item: any) : string {
        return item.DisplayOption === "" ? "fullwidth" : item.DisplayOption; 
    }

    resolve(item: any): ContentAreaItem {
        var contentType: string = this.getType(item);

        var i = new ContentAreaItem();

        i.Component = this.components[contentType];
        i.ItemClasses = this.getDisplayOption(item);

        return i;
    }
}

Schritt 3 – Hosten der Website mit Vercel

Vercel ist eine Plattform für Websites, die mit Frontend-Frameworks erstellt wurden. Wenn Ihre Website mit Vercel gehostet wird, erhalten Sie aufgrund des Edge-Cachings automatisch Leistungsvorteile.

Einsatz

Die Bereitstellung Ihrer Website mit Vercel ist äußerst unkompliziert.

Erstellen Sie ein neues Projekt und verbinden Sie es mit dem GitHub-Repository und konfigurieren Sie den Quellspeicherort und die Build-Pipeline. Jedes Mal, wenn der Zweig aktualisiert wird, wird der Code erstellt und die Website automatisch bereitgestellt.

Dieser Ansatz hat einige echte Vorteile:

  1. Die Vorschau aller Änderungen ist einfach. Jeder Push an das Repository löst einen Build aus und generiert eine eindeutige URL, die geteilt werden kann.
  2. Es ist möglich, eine frühere Version auf „Produktion“ hochzustufen, d. h. einfach durch Klicken auf eine Schaltfläche zurückzusetzen.

Schritt 4 – Handhabung von Inhaltsänderungen

Statische Websites können eine atemberaubende Leistung erbringen, stellen jedoch Herausforderungen dar, wenn Inhalte geändert werden. Die Änderungen werden nicht wiedergegeben.

Es gibt mehrere Strategien, die Sie anwenden können, um dieses Problem zu lösen.

  1. Verlängerung auf Anfrage – Es ist möglich, eine Seite neu zu generieren, wenn eine Anfrage eingeht, aber so gedrosselt, dass eine X-Anzahl von Sekunden verstrichen sein muss, bevor die Seite neu generiert werden kann.
  2. On-Demand-Neuvalidierung – Sie können einen API-Endpunkt verfügbar machen, der beim Aufruf die spezifische Ressource neu generiert.

Das Problem mit Verlängerung auf Anfrage Wir bewegen uns von der statischen Generation zur dynamischen Generation.

import type { NextApiRequest, NextApiResponse } from 'next'

type ErrorData = {
  message: string
}

type SuccessData = {
    revalidated: boolean,
    message: string
  }

export default async function handler(
    req: NextApiRequest,
    res: NextApiResponse
) {
    if (req.query.secret !== process.env.REVALIDATE_TOKEN) {
      return res.status(401).json({ message: 'Invalid token' })
    }
  
    const { revalidatePath } = req.body;

    try {
      await res.revalidate(revalidatePath)
      return res.json({ message: revalidatePath, revalidated: true })
    } catch (err) {
      return res.status(500).send({ message: 'Error revalidating :' + revalidatePath })
    }
  }

Im obigen Beispiel habe ich einen API-Endpunkt verfügbar gemacht. Der Anforderungstext enthält den Pfad der Ressource, die ungültig gemacht werden muss. Der ‘erneut validieren‘-Funktion löst dann die Neugenerierung der Seite aus.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IContentEvents contentEvents)
{
        contentEvents.PublishedContent += ContentEvents_PublishedContent;
}

private void ContentEvents_PublishedContent(object sender, ContentEventArgs e)
{
        if (e.Content is IRoutable routableContent)
        {
            var url = UrlResolver.Current.GetUrl(e.ContentLink);

            Task.Run(() =>
            {
                var request = new RevalidateRequest { RevalidatePath = url };

                Task.Delay(10000);  // wait 10 seconds

                var r = client.PostJsonAsync("/api/revalidate/?secret=...", request);

                Task.WaitAll(new[] { r });
            });
        }
}

Der obige C#-Code zeigt, wie die Optimizely-Website die Revalidierung auf der statischen Website auslöst. Ich habe eine 10-Sekunden-Verzögerung eingebaut, da Sie zulassen müssen, dass der Inhalt mit dem Inhaltsdiagramm synchronisiert wird.

Abschließende Gedanken

Auch wenn Sie Ihre Inhalte wahrscheinlich nicht so reproduzieren werden, wie Sie es auf einer normalen Optimizley-Website tun würden, demonstriert dieser POC die Kernkonzepte bei der Verwendung von Optimizely als Headless-CMS.

Leistungsvorteile

Obwohl dies nicht der wissenschaftlichste aller Vergleiche ist, zeigen die beiden folgenden Leuchtturmberichte die Leistungsverbesserungen, die Sie erzielen können, wenn Sie zu einem statisch generierten Ansatz wechseln.

Nächster Artikel

Im nächsten Artikel werde ich mich mit der Verwendung von Optimizely in einem Headless-Modus befassen und auch andere Funktionen wie Suche, Inhaltsauflistung usw. demonstrieren.

Beispiele

Sie können auf den POC-Quellcode über mein GitHub-Konto und die statische Website unter https://graph-ql-three.vercel.app/ zugreifen.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.