Come sviluppare questo sito: Next.js, Fermo, Gatsby, Middleman. Appunti su cosa imparo.
Ho deciso di aprire un blog; i motivi li spiego nella pagina Perchè questo sito
Dopodiché c'è da svilupparlo e le opzioni davanti a me sono numerose. Una chiacchierata con l'amico Cecio fu galeotta: parlavamo dello stato dell'arte del mondo CMS, HeadlessCMS in particolare e delle scelte ottimali per la realizzazione del suo sito personale. E se il sito fosse il mio, quali tecnologie sceglierei? Quali compromessi? E nel cercare le risposte, è nata anche la voglia di provarci!
Conosco l'ambito web molto bene, è il mio lavoro. Ma scegliere in modo pragmatico, mettendo sul piatto il tempo da dedicare al progetto, una visione a lungo termine e le competenze personali a disposizione (perché te lo devi fare da te e non hai un team di esperti allo sviluppo su cui contare), non è affatto banale.
Per inziare ho definito alcuni assunti:
Ho due tipi di contenuti: pubblici e privati e voglio che finiscano su due canali differenti: i pubblici su un un blog online; i privati (preferibilmente in un formato markdown), in locale e/o su un git privato; comunque in uno spazio cloud accessibile in futuro per i miei familiari.
Per scrivere e organizzare entrambe le tipologie di contenuto, uso DatoCMS. È un prodotto che ho contribuito a far nascere e funziona maledettamente bene.
Per sviluppare il frontend, ho deciso di divertirmi ad imparare qualcosa che conosco ma di cui ho poca pratica diretta, perché il mio lavoro in Cantiere Creativo è ormai più strategico/organizzativo che operativo. Col codice sono rimasto troppo indietro, la lacuna è da colmare!
Quale generatore usare?
Se il CMS è chiaro, l'SSG ha richiesto un po' di riflessioni in più.
Conosco approfonditamente Middleman che ho uso dal 2014 per fare progetti di dimensioni grosse, come il sito del Ministero per l'Innovazione Digitale, che ridotte come Acacia.
Middleman è potente e fatto bene e ha una serie di caratteristiche che apprezzo particolarmente:
È scritto in ruby, linguaggio con cui ho confidenza, e posso usare il vasto ecosistema di gemme
Permette di usare Slim, un markup che adoro.
Ha un sistema di partials e layout che permettono di costruire dei componenti riutilizzabili.
Posso inizializzare un nuovo progetto e averlo online su Netlify in circa 30 secondi.
Offre un sistema di proxy per gestire i template in modo separato ed elegante.
Il server locale costruisce la singola pagina al volo al momento della richiesta. La connessione live al CMS restituisce i contenuti aggiornati. Infine il sistema livereload ricarica la pagina automaticamente ad ogni salvataggio di contenuto, html o css. Lo sviluppo, una festa.
Di contro ha un grosso problema di performance: la build finale è lenta e con l'aumentare dei contenuti non può che andare a peggiorare. Un sito di medie dimensioni può richiedere circa 6 minuti per il deploy, ma non è raro vedere build da oltre 20 minuti. Senza un sistema di preview, per gli editor diventa una situazione davvero inaccettabile, è solo una questione di tempo.
Le alternative
Ho scartato il trendissimo Gatsby con cui non ho mai avuto a che fare: onestamente ho sentito troppi moccoli intorno a questo framework per cui non mi azzarderei. Gatsby è di fatto un SSG, anche se usa React, i problemi di performance in fase di deploy non migliorano e se provi ad uscire dal seminato, avrai sicuramente problemi.
Hugo è potente in termini di performance, ma lo sviluppo è una pena quando sei abituato alle caratteristiche di Middleman. Jeckyll, non lo prendo proprio in considerazione, sembra preistoria.
Ho quindi due alternative, e in qualche modo le userò entrambe: Next.js per il blog e Fermo per i contenuti privati.
Fermo
È un framework OpenSource scritto in Elixir concepito da Joe Yates, amico e collega di una vita, nonché CTO in Cantiere Creativo. Stanchi di cercare di migliorare le performance di Middleman e dopo aver creato un CMS per siti statici, creare un generatore di siti statici è venuto quasi naturale. Elixir sta diventando quasi uno standard in Cantiere, proprio per le incredibili performance che garantisce. Fermo è stato scritto "copiando" tutto ciò che di buono c'è in Middleman, ma ottimizzando alcune lacune.
Fermo is faster than the fastest Static Site Generator you can think of when building big, complex websites. Yes, even *that* one.
Fermo è ancora in una release alpha, anche se lo abbiamo usato già in numerosi progetti in produzione. Sfrutta i multiprocessori per generare le pagine, permette di fare query in GraphQL, sfruttare le magie delle immagini responsive fornite da DatoCMS, permette di avere un sistema di preview. Elixir non è diffuso come Ruby, ma ha comunque una vasta gamma di librerie (chiamate mix) per cui abbiamo Slime (corrispettivo di Slim), Sass e tutto ciò che serve.
Userò Fermo per costruire i file Markdown in locale. Capisco che possa sembrare insensato avere due applicazioni separate per fare cose simili. Ma tanto simili non sono: i contenuti hanno accesso differente, l'output è completamente differente e, cosa più importante, voglio divertirmi ad usarlo 😉
Next.js
È in hype totale in questo ultimo anno e non c'è da stupirsi. Fatto bene, con idee innovative alla base. Con Next.js posso sfruttare il sistema isomorfo per avere sia contenuti statici che dinamici, serviti da un potente sistema di cache. Con Vercel, l'esperienza di deploy è ormai pari a quella di Netlify. Inoltre DatoCMS e Next.js sono buoni amici, il supporto è totale in entrambi i sensi.
Di contro:
😢 Non ho esperienza con React, se non teorica per osmosi in ufficio
😢 Non posso usare Slim
😢 Se consumo tanto tempo ad imparare Next/React, non posso mettermi a fare anche il css come piace a me, usando BEMO come base e poi scritto di cesello, ad-hoc con Bem. Devo usare un framework tipo Bootstrap, Tailwind ecc.
Al contempo sono contento perché:
Posso sperimentare sulla mia pelle e comprendere a fondo qualcosa che, di fatto, vendo e consiglio tutti i giorni. È sempre bello imparare qualcosa di nuovo.
Alcuni aspetti dei componenti React mi piacciono un sacco e le possibilità sono notevoli lato frontend.
Posso partire da uno starter kit di Dato e di fatto questo sito è andato online in poco più di 30 secondi.
Dopodiché, c'è la personalizzazione, e qui viene il bello!
WIP: Cosa sto imparando?
Prenderò qui appunti delle varie cose che imparo.
CSS
Antecedenti: ho odiato Bootrstrap, molto, per molto tempo, a favore del Bem che salvò i frontendisti in Cantiere Creativo: css mantenibili negli anni, svincolo da un unico sviluppatore, non più migliaia di righe di css ma singoli blocchi indipendenti, controllo totale dello stile. ✌️Vittoria indiscussa su tutti i fronti.
Ho dovuto usare Bootstrap per il sito del Midt, e nei mesi ho apprezzato numerosi aspetti. I sistemi utiltiy-first ti obbligano ad usare una quantità insopportabile di classi per ogni tag senza contare il fatto che, in caso di personalizzazioni, devi necessariamente andare di override e !important, il male assoluto.
Poiché ho imparato Bootstrap usandolo sul campo, sarebbe stata per me la scelta più ovvia; ma non può andare sempre tutto liscio: lo starter kit del blog utilizza Tailwind, mai visto prima. Ho speso un po' di tempo per leggere la documentazione, ho fatto qualche prova e devo dire che mi ha convinto: sembra più snello e più centrato nel suo obiettivo di essere utility first. Inoltre offre una discreta libreria di componenti, già pronti anche in React. Dopo le prime personalizzazioni, non l'ho odiato — il che non è poco! Vamos 🦾
GraphQL
Capisco che la query debba essere fatta dentro ogni pagina, mentre i componenti non possono avere query ma solo ricevere i dati. dalla pagina. Qui iniziano i primi dubbi:
Come gestisco il passaggio dei dati in sotto componenti? Alla fine non si perde di chiarezza?
C'è un modo per evitare di ripetere query identiche in ogni pagina?
Una via possono essere i fragment, ovvero porzioni di query che possono essere richiamate dentro la query di una pagina. Ma non era questo che mi interessava. Ho capito che posso creare dentro la directory lib (o utils, a preferenza) dei file/funzioni a cui delegare sia la query che la creazione dei dati necessari a creare un componente.
Ho usato questo sistema per generare l'elenco di categorie in cui è presente almeno un articolo. Questo componente è presente in diverse pagine, e avrei dovuto fare la query in ognuna di esse.
Quindi ho creato il file lib/activeCategories.js
import { request } from './datocms';
const query = `
{
allCategories {
description
id
name
slug
}
allPosts(filter: {isPublic: { eq: true }}) {
category{
id
}
}
}
`;
export default async() => {
const result = await request({ query })
const { allPosts, allCategories } = result
const categories = allCategories.map(cat => {
const matching = allPosts.filter(p => p.category.id == cat.id)
return matching.length > 0 ? cat : null
})
const catsWithPosts = categories.filter(c => !!c)
return catsWithPosts
}
Là dove lo voglio usare basta richiamare
const categories = await activeCategories()
Per poi renderizzarlo così
<ul className="flex flex-row flex-wrap my-8">
{categories.map(cat =>
<li className="mb-3 mr-3 rounded-md bg-green-500 text-white flex items-center justify-center md:text-lg lg:text-2xl font-bold rounded-t-xl p-2 px-4">
<Link as={`/categories/${cat.slug}`} href="/categories/[slug]">
<a className="hover:underline">{cat.name}</a>
</Link>
</li>
)}
</ul>
Il tutto per ottenere questo
Errori babbani:
Non commentare le righe nelle query. Perderai ore a cercare il bug nella query quando l'errore è solo quel dannato commento. 😤
React
È importante capire la differenza tra funzioni "normali" e le funzioni asicnrone. Ho un po' sottovalutato questo aspetto. Anche se può sembrare datato, un articolo interessante di Bob Nystorm è "What color is your function" può aiutare.
Iframes
Come embeddare iframes in modo corretto e con un occhio a sicurezza e performance. Leggi chi ci è passato prima di me!