Un site static pare o potrivire bună pentru un proiect mic și constant, nu? Ca unul care nu necesită funcții avansate sau interacțiune cu utilizatorii. Dar cum puteți profita de avantajele de performanță și puteți face ca site-ul dvs. static să fie dinamic, personalizat și interactiv?

Ori de câte ori menționez un „site static” pentru dezvoltatorii care nu au funcționat încă cu generatoare de site-uri statice, aceștia se încruntă. Cuvântul buzz funcționează împotriva mea și nu descrie cu adevărat ce obțineți dacă decideți să utilizați un generator de site static (cum ar fi Gatsby sau Gridsome).

Așadar, le explic cum funcționează totul, inclusiv reconstrucțiile automate atunci când conținutul sau implementarea se schimbă. Au întotdeauna același comentariu:

“Este frumos și totul, dar pre-generarea site-ului nu va funcționa pentru scenarii dinamice, cum ar fi comerțul electronic sau personalizarea. Astfel, este bun doar pentru proiectele mici.”

Și asta nu este adevărat. Îți voi arăta de ce.

Cum sa va dinamizati site ul static

Există două moduri de a dinamiza un site static:

  • în timpul redării site-ului
  • prin interacțiunile utilizatorilor de pe site

Ar trebui să explic acest lucru folosind un site web real. În ultima vreme, m-am confruntat cu sarcina de a crea un site de nuntă. Știu, există mii de șabloane simple pentru asta. Dar, fiind angajați în IT, oamenii se așteaptă implicit ca site-ul să fie de ultimă generație. Așa că am cedat. Le voi arăta.

ad-banner

Pentru partea de implementare am decis să folosesc un generator de site static Gridsome, deoarece prefer Vue.js decât React. Voi folosi un CMS fără cap pentru a stoca conținutul și două funcții fără server pentru a gestiona interacțiunea cu utilizatorul.

Preferați videoclipul? Urmăriți Seriale Twitch pe YouTube. Și asigurați-vă că urmărește-mă pe Twitch pentru a prinde toate fluxurile viitoare.

Conținut dinamic în timpul redării site-ului

Am reunit toate informațiile pe care le știu înainte de a construi site-ul web. Știu pe cine vreau să invit. Știu când are loc evenimentul și știu cu cine mă căsătoresc. La fel cum știi ce produse vrei să vinzi sau ce servicii vrei să oferi pe site-ul tău.

Având în vedere acest lucru, am creat o serie de modele de conținut pentru site-ul meu:

  • Invitat
  • Cazare
  • Secțiune
  • Element cronologic

Și așa arată în designul real al site-ului:

1611300310 78 Cum sa va dinamizati site ul static

Deoarece știu toți invitații, am folosit conținutul din CMS fără cap pentru a genera (automat) o pagină separată pentru fiecare invitat (verificați eticheta URL personalizată din imagine). Ca urmare, la momentul construirii, componentele cunosc contextul invitatului. Imaginați-vă posibilitățile de personalizare – pot chiar să returnez un 404 pentru unele dintre rudele mele cele mai puțin preferate.

De fapt, l-am folosit pentru a afișa salutări personalizate și numai elemente cronologice relevante.

1611300310 972 Cum sa va dinamizati site ul static

Dacă creați un site de comerț electronic, puteți implementa o pagină de produs care afișează o listă de produse similare. De asemenea, probabil că ați face legătura cu acele servicii relevante pentru produsele pe care le oferă compania dvs. Știți toate detaliile necesare la momentul construirii.

Modelarea conținutului este cheia site-urilor de pre-construire

Am identificat trei modele de conținut pentru site-ul meu, dar de obicei este mult mai mult decât atât. O modalitate bună de abordare a modelării conținutului este să aruncați o privire asupra wireframe-urilor pentru viitorul dvs. site. Nu este vorba doar despre cum să introduceți datele în CMS, trebuie să vă gândiți la:

  • Cum va fi afișat și consumat conținutul?
    Luați de exemplu produse și categorii. În majoritatea cazurilor, le veți găsi într-o relație N: N, dar eu vizez partea de implementare a lucrurilor aici. Gândiți-vă cât de complicate vor fi interogările de date. Ajustarea modelelor de conținut pentru a reprezenta mai bine structura reală a site-ului poate ajuta foarte mult la implementare.
    În exemplul meu, elementele cronologiei sunt legate de invitați (1: N), ceea ce permite o implementare simplă, în timp ce gestionarea conținutului este încă simplă. Ca și reorganizarea ordinii articolelor.
1611300310 796 Cum sa va dinamizati site ul static
  • Cum este conținutul legat de alte elemente de conținut?
    Care este relația dintre produse, pachete de produse, categorii, oferte speciale sau reduceri? Răspunsurile la aceste întrebări vă vor ajuta să alegeți instrumentul potrivit pentru conectarea articolelor de conținut, cum ar fi Taxonomie sau elemente legate.
  • Cum va fi creat conținutul?
    Editorii vor înțelege structura conținutului pe care îl aveți la dispoziție? De asemenea, în majoritatea cazurilor, nu au acces la proiecte întregi, ci doar la părți relevante pentru acestea. Structura dvs. permite un nivel suficient de granularitate a permisiunilor? Modelele dvs. de conținut sunt suficient de restrictive pentru a evita lipsa problemelor de conținut pe site-ul live?

Există mult mai mult în modelarea conținutului. Dacă sunteți interesat, aruncați o privire la această mare serie despre modelarea conținutului compus de Michael Kinkaid.

Componente dinamice

Deci, cu modelele de conținut potrivite, putem genera site-ul static. Ei bine, pre-generarea este probabil o etichetă mai bună pentru aceasta. Conținutul său nu este vechi și static – fiecare modificare a conținutului va reconstrui efectiv site-ul din nou.

Dar dacă trebuie să interacționăm cu vizitatorii? Uneori trebuie să obținem informații de la ei sau să le arătăm conținut diferit în funcție de acțiunile lor. În aceste cazuri, putem folosi componente dinamice. Acestea sunt pre-inițializate cu valori în timpul construcției site-ului, dar pot continua să interacționeze cu sistemele de backend pe baza acțiunilor vizitatorilor.

1611300310 983 Cum sa va dinamizati site ul static

Pe site-ul meu web, am un formular pe care invitații îl pot utiliza pentru a confirma ce tip de cazare sunt interesați. Selecția lor trebuie să fie stocată în același articol de conținut invitat pe care l-am creat inițial în CMS fără cap.

1611300310 733 Cum sa va dinamizati site ul static

Aș putea comunica cu CMS direct din componenta de pe site. Cu toate acestea, vorbim despre JavaScript de partea clientului aici. Expunerea cheii ar fi o problemă majoră de securitate, chiar dacă nu mă aștept ca niciunul dintre invitații mei să înțeleagă ce este o cheie de securitate sau cum poate fi utilizată greșit. Deci, omul de mijloc dintre site-ul static și CMS este o funcție fără server.

Componentă reactivă pe un site static

Să începem cu componenta. Am folosit Vue.js și Gridsome ca SSG, dar conceptul de componentă dinamică este același indiferent de cadrul utilizat. CMS-ul fără cap pe care l-am folosit aici este Conformitate. Are un nivel gratuit generos, dar dacă îți place open-source-ul (ca să citez sistemele mele de operare profesor universitar „Nu am încredere decât dacă îi văd codul”) am auzit Strapi este o alegere bună.

Implementarea componentelor

La momentul construirii, componenta va primi date inițiale – datele pe care le cunoaștem în acel moment specific din timp. Dacă Michael a selectat una dintre opțiuni săptămâna trecută și reconstruim site-ul astăzi, știm selecția sa.

<RsvpAccommodation inviteeId="{GUID}" optionSelected="sleep_in_a_tent" howManyInvited="2" salutation="Michael" />

Pe de altă parte, dacă nu a interacționat încă cu site-ul, selecția ar fi goală.

<RsvpAccommodation inviteeId="{GUID}" optionSelected="" howManyInvited="2" salutation="Michael" />

Componenta arată astfel:

<template>
    ...
    <input type="radio" name="option" value="not_interested" id="none" v-model="option" />
    <label for="none">Děkuji, nepotřebuji</label>
    <input type="radio" name="option" value="interested_in_booking_a_room" id="hotel" v-model="option" />
    <label for="hotel">Mám zájem o ubytování v okolí</label>
    <input type="radio" name="option" value="sleep_in_a_tent" id="tent" v-model="option" /><label for="tent">Mám zájem o přespání ve vlastním stanu</label>
    ...
</template>
<script>
export default {
    props: {
        salutation: String,
        inviteeId: String,
        howManyInvited: Number,
	salutation: String,
        optionSelected: String
    },
    data: function(){
        option: this.optionSelected
    },
    ...
</script>

Vue.js urmărește proprietățile datelor utilizate. Când Michael își schimbă selecția, evenimentul de schimbare a datelor este declanșat. Rețineți că numele proprietății din obiectul ceas trebuie să se potrivească cu numele proprietății de date.

În acel moment, trebuie să stocăm selecția sa – formăm datele și facem o cerere asincronă către funcția fără server – toate folosind JS de partea clientului.

...
<script>
export default {
    ...
    watch: {
        option: function(newVal, oldVal) {
            let url = `{remote base URL}/action?id=${this.inviteeId}`;
            fetch(url, {
                method: 'POST',
                body: JSON.stringify({
                    option: this.option,
                })
            })
            .then(response => {
                if (response.status !== 200) {
                    alert("Unable to save, please try again.");
                }
            });
         }
    }
}
</script>

Implementarea funcției fără server

Am folosit Netlify pentru a construi și implementa funcția fără server. Dacă aceasta este prima dvs. funcție Netlify, nu ezitați să aruncați o privire asupra mea video de introducere unde arăt cum se configurează mediul de dezvoltare local Netlify.

CMS fără cap are două API-uri. Una pentru livrarea datelor – am folosit-o pe aceea pentru a obține toate datele în timpul construirii site-ului – și alta pentru gestionarea datelor. În funcția fără server, trebuie să folosesc ambele API-uri, așa că am adăugat ID-ul proiectului și cheia API de gestionare în fișierul .env din rădăcina proiectului de funcții Netlify:

KONTENT_PROJECT_ID={project ID}
KONTENT_CM_KEY={management API key}

Și este întotdeauna mai plăcut să folosești un SDK decât să te lupți cu apelurile goale REST API:

npm i @dotenv --save
npm i @kentico/kontent-delivery --save
npm i @kentico/kontent-management --save

Începutul funcției arată astfel:

require("dotenv").config();
const KontentDelivery = require('@kentico/kontent-delivery')
const KontentManagement = require('@kentico/kontent-management')

Funcția este disponibilă mai mult sau mai puțin public – URL-ul său este stocat în codul JS al clientului în text simplu – deci trebuie mai întâi să facem câteva verificări elementare. Fiecare cerere pentru această funcție trebuie să conțină un parametru ID în șirul de interogare care deține un identificator al unui invitat existent. Aceasta este persoana care a completat formularul. Dacă ID-ul lipsește sau este nevalid, returnăm 404.

exports.handler = async (event, context, callback) => {
  const { KONTENT_PROJECT_ID, KONTENT_CM_KEY } = process.env;
  const deliveryClient = new KontentDelivery.DeliveryClient({ projectId: KONTENT_PROJECT_ID });
  let id = event.queryStringParameters.id;
  const invitee = await deliveryClient.items()
                    .type('invitee')
                    .elementsParameter(['accommodation'])
                    .equalsFilter('system.id', id)
                    .toPromise();
  if (invitee.items == null || invitee.items.length == 0)
  {
    return {
      statusCode: 404,
      body: `Invitee not found`
    };
}

Solicitarea de livrare a clientului este limitată doar la un singur element – accommodation. Acest lucru se datorează faptului că informațiile din formular nu sunt stocate în Invitat model, dar într-un element de tip legat Cazare.

1611300310 883 Cum sa va dinamizati site ul static

Modelul de conținut Cazare corespunde direct formularului de pe site:

1611300310 258 Cum sa va dinamizati site ul static

Vrem să stocăm datele obținute de la clientul JS ca o nouă înregistrare. Actualizarea elementului de conținut existent este, de asemenea, o posibilitate, dar am pierde tot istoricul în cazul în care invitații își schimbă selecția în viitor.

În primul rând, notăm ID-ul existentului Cazare element de conținut și inițializați clientul de gestionare a conținutului.

let accommodationId = invitee.items[0].accommodation.value[0].system.id;
const client = new KontentManagement.ManagementClient({ projectId: KONTENT_PROJECT_ID, apiKey: KONTENT_CM_KEY });

Apoi, trebuie să creăm o nouă variantă lingvistică a Cazare element de conținut. Chiar dacă există o singură limbă în proiect, conținutul este stocat într-un compartiment separat etichetat cu numele de cod al limbii respective. Acest lucru asigură o tranziție lină dacă decideți să adăugați limbi suplimentare în viitor.

await client.createNewVersionOfLanguageVariant()
      .byItemId(accommodationId)
      .byLanguageCodename('default')
      .toPromise();

Acest cod face același lucru ca și când dați clic pe „Creați o nouă versiune” în interfața de utilizare.

1611300310 305 Cum sa va dinamizati site ul static

Apoi, trebuie să completăm varianta cu date. Datele apar ca JSON în corpul cererii.

let accommodation = JSON.parse(event.body);
await client.upsertLanguageVariant()
    .byItemId(accommodationId)
    .byLanguageCodename('default')
    .withElements([{
        element: { codename: 'option' },
        value: [{ codename: accommodation.option }]
     }])
    .toPromise();

Ultimul pas este publicarea noii variante:

await client.publishOrScheduleLanguageVariant()
    .byItemId(accommodationId)
    .byLanguageCodename('default')
    .withData()
    .toPromise();
return { statusCode: 200, body: `OK` }

Probleme CORS cu Netlify

Chiar dacă rulați local funcțiile și site-ul static, veți da peste o problemă CORS, deoarece ambele implementări sunt servite din porturi diferite. La toate răspunsurile din funcția fără server, trebuie să returnați antetul „Access-Control-Allow-Origin”.

Netlify are un mod simplu de a gestiona acest lucru la nivel global prin intermediul netlify.toml fișier de configurare în rădăcina proiectului de funcții:

[build]
  Functions = "lambda"
[[headers]]
  for = "/*"
  [headers.values]
    Access-Control-Allow-Origin = "*"

Date vechi după reîmprospătarea paginii

Acum componenta reacționează la acțiunile vizitatorilor. Cu toate acestea, starea inițială (care va fi afișată și în cazul în care vizitatorul reîmprospătează pagina) provine în continuare din construcția statică a site-ului. Dacă un vizitator își modifică selecția, modificarea este salvată în CMS, dar site-ul nu este reconstruit.

Nu ar fi eficient să reconstruiți întregul site după fiecare interacțiune cu utilizatorul. Chiar dacă am face acest lucru, ar dura câteva secunde până la minute până când construirea și implementarea vor fi finalizate.

În schimb, facem o cerere asincronizată atunci când componenta este redată pentru prima dată:

<script>
...
    mounted: function () {
        let url = `${baseUrl}/delivery?id=${this.inviteeId}`;
        let response = fetch(`{remote base URL}/delivery?id=${this.inviteeId}`, { method: 'GET', mode: 'cors' })
            .then(response => response.json())
            .then(accommodationObj => {
                this.option = accommodationObj.option;
            });
    },
...
</script>

Componenta va fi pre-inițializată cu date în timpul pre-construcției site-ului. Dar odată ce componenta este creată, va primi date noi de la CMS utilizând o altă funcție fără server. Această funcție este foarte asemănătoare cu cea anterioară:

exports.handler = async (event, context, callback) => {
  const { KONTENT_PROJECT_ID } = process.env;
  let id = event.queryStringParameters.id;
  const deliveryClient = new KontentDelivery.DeliveryClient({ projectId: KONTENT_PROJECT_ID });
  const invitee = await deliveryClient.items()
                    .queryConfig({ waitForLoadingNewContent: true })
                    .type('invitee')
                    .elementsParameter(['accommodation', 'option'])
                    .equalsFilter('system.id', id)
                    .toPromise();
  if (invitee.items == null || invitee.items[0] == null)
  {
    return {
      statusCode: 404,
      body: `Invitee not found`
    };
  }
  return {
	statusCode: 200,
	body: JSON.stringify({
		option: invitee.items[0].accommodation.value[0].codename
	})
  };
};

În acest caz, trebuie să adăugăm o configurație suplimentară la interogarea de date – waitForLoadingNewContent. Conținutul provenit de la CMS fără cap este stocat în cache și livrat prin CDN, astfel încât este posibil să obținem conținut învechit dacă a fost modificat în ultimele minute. Opțiunea de configurare asigură că răspunsul va conține întotdeauna date noi.

Deci, procesul general al unei componente dinamice pe un site static arată astfel:

1611300310 436 Cum sa va dinamizati site ul static

Este rapid și interactiv

Vedeți, avantajul mare pe care îl aduc site-urile statice este că toate informațiile disponibile la momentul construirii pot fi servite ca fișiere statice, care sunt rapide și ușor scalabile utilizând un CDN.

Dar pot oferi și funcționalități dinamice care pot fi livrate prin funcții fără server – de asemenea ieftine și ușor scalabile.

Dacă luați site-ul meu ca exemplu – în loc să trebuiască să implementez întreaga aplicație în cloud, am avut nevoie doar să găzduiesc o grămadă de fișiere statice mici și două mici funcții fără server. Și, de asemenea, sunt capabil să scalez aceste funcții independent.

Este posibil ca site-urile statice să nu fie alegerea finală pentru dezvoltarea web, dar pentru multe proiecte aduce claritate, performanță și beneficii de securitate, precum și costuri reduse de întreținere. Care este experiența ta? Să-mi dai de veste.

Asigurați-vă că verificați și fișierul meu Fluxuri de contracție și Canalul canalului YouTube despre dezvoltarea web.