Lo sviluppo web ha fatto molta strada rispetto ai tempi in cui i siti personali erano statici e a pagina singola. Oggi abbiamo una pletora di linguaggi, framework e sistemi di gestione dei contenuti creati per soddisfare ogni possibile esigenza.

Tra i framework JavaScript, Astro è uno degli ultimi arrivati.

Creato da Fred K. Schott e da un gruppo di collaboratori, Astro è diventato rapidamente uno dei framework preferiti dalla comunità di sviluppatori. Si tratta di un framework all-in-one che funziona come generatore di siti statici.

In questo articolo spiegheremo perché molti sviluppatori apprezzano Astro e lo preferiscono ad altre soluzioni. Spiegheremo anche come costruire un blog basato su markdown.

Cos’è Astro?

Il logo Astro in nero, con
Astro

Astro, o Astro.js, è un popolare generatore di siti statici pensato per chi vuole creare siti ricchi di contenuti veloci e leggeri. La struttura è intuitiva e la curva di apprendimento bassa. Ciò lo rende interessante per sviluppatori di ogni livello.

Nonostante le dimensioni ridotte, Astro è dotato di potenti strumenti che aumentano drasticamente la flessibilità di un sito, facendo risparmiare ore nella gestione dei contenuti e dei temi. Inoltre, in combinazione con Astro, gli sviluppatori hanno la possibilità di lavorare con i propri framework preferiti: una prospettiva interessante per i programmatori esperti.

Ecco alcune caratteristiche che distinguono Astro dalla massa:

  • Architettura a isola: Astro suddivide l’interfaccia utente (UI) in elementi più piccoli e isolati, noti come “Astro Islands”, che possono essere utilizzati in qualsiasi pagina. Il codice JavaScript non utilizzato viene sostituito da un HTML leggero.
  • Zero JavaScript (di default): anche se è possibile utilizzare tutto il codice JavaScript che si vuole, Astro cercherà di distribuire zero JavaScript in produzione trascrivendo il codice per voi. È un approccio perfetto se l’obiettivo è la velocità del sito.
  • SSG e SSR: Astro è nato come generatore di siti statici, ma nel corso del tempo è diventato un framework che utilizza sia la generazione di siti statici (SSG) che il rendering lato server (SSR). Ed è possibile stabilire l’approccio da seguire per ogni pagina.
  • Framework-agnostico: con Astro è possibile utilizzare qualsiasi framework JavaScript, anche più framework contemporaneamente (Ne parleremo in modo più dettagliato più avanti in questo articolo).

Inoltre, Astro è edge-ready, cioè può essere distribuito ovunque e in qualsiasi momento.

Vogliamo indagare più a fondo? Scopriamo come funziona Astro.

La struttura di Astro

Prima di approfondire, è importante capire come è strutturato Astro per poterlo utilizzare in modo efficace. Diamo un’occhiata alla struttura dei file core di Astro:

├── dist/
├── src/
│   ├── components/
│   ├── layouts/
│   └── pages/
│       └── index.astro
├── public/
└── package.json

Come vedete, la struttura in sé è piuttosto semplice. Ma ci sono alcuni punti chiave da ricordare:

  • La maggior parte del nostro progetto si trova nella cartella src. È possibile organizzare i componenti, i layout e le pagine in sottocartelle. È anche possibile aggiungere altre cartelle per rendere più semplice la navigazione del progetto.
  • La cartella public contiene tutti i file che vivono al di fuori del processo di build, come i font, le immagini o il file robots.txt.
  • La cartella dist conterrà tutti i contenuti che si desidera distribuire sul server di produzione.

Ora analizziamo gli elementi principali di Astro: i componenti, i layout e le pagine.

I componenti

I componenti sono blocchi di codice riutilizzabili che possono essere inseriti in tutto il sito, simili agli shortcode di WordPress. Di default hanno l’estensione .astro, ma è possibile utilizzare anche componenti non Astro, ad esempio componenti Vue, React, Preact o Svelte.

Ecco come si presenta un semplice componente: in questo caso, un tag div contenente un h2:


<div class="kinsta_component">
    <h2>Hello, Kinsta!</h2>
</div>

Ed ecco come incorporare questo componente nel nostro sito:

---
import KinstaComponent from ../components/Kinsta.astro
---
<div>
    <KinstaComponent />
</div>

Come mostrato sopra, bisogna prima importare il componente. Solo allora potrà essere incluso nella pagina.

Ora è il momento di aggiungere alcune proprietà al componente. Iniziamo con la proprietà {title}:

---
const { title = 'Hello' } = Astro.props
---

<div class="kinsta_component">
    <h2>{title}</h2>
</div>

Ecco come verrà implementata la nostra proprietà:

---
import KinstaComponent from ../components/Kinsta.astro
---

<div>
    <!-- This shows "Good day" -->
    <KinstaComponent title="Good day"/>

    <!-- This shows "Hello" -->
    <KinstaComponent />
 </div>

Semplice, vero?

Come probabilmente avrete già capito, la vera forza dei componenti di Astro sta nella loro natura globale e riutilizzabile. Permettono di apportare modifiche radicali all’intero sito intervenendo solo su poche righe di codice, il che potrà farvi risparmiare il tempo richiesto da noiose e minuziose sostituzioni di testo.

I layout

Parliamo ora dei layout. Oltre alla loro nota funzione tematica, anche i layout in Astro sono componenti riutilizzabili, ma vengono impiegati come involucri di codice.

Ecco un esempio:

---
// src/layouts/Base.astro
const { pageTitle = 'Hello world' } = Astro.props
---

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width">
    <title>{pageTitle}</title>
</head>
<body>
    <main>
        <slot />
    </main>
</body>
</html>

Si noti l’elemento <slot />. In Astro, questo funge da segnaposto per i tag e i contenuti HTML veri e propri.

Vediamolo in azione.

Il codice qui sotto mostra il nostro tag <slot /> che viene sostituito con il codice desiderato, il tutto racchiuso nel nostro layout Base.astro:

---
import Base from '../layouts/Base.astro';
---

<Base title="Hello world">
    <div>
        <p>Some example text.</p>
    </div>
</Base>

Il tag <slot /> è stato sostituito dall’HTML che rappresenta, ovvero:

<div>
    <p>Some example text.</p>
</div>

I layout, come i componenti, permettono di riutilizzare blocchi di codice in tutto il sito. Questo permette di aggiornare il contenuto e il design globale in modo molto più semplice.

Pagine

Le pagine sono un tipo speciale di componente responsabile del routing, del caricamento dei dati e del templating.

Per generare le pagine, Astro utilizza un routing basato su file, invece di un routing dinamico. Il metodo basato sui file non solo consuma meno banda, ma evita anche di dover importare manualmente i componenti.

Ecco un esempio di definizione dei percorsi:

src/pages/index.astro => yourdomain.com
src/pages/test.astro => domain.com/test
src/pages/test/subpage => domain.com/test/subpage

Con questi percorsi, la nostra homepage verrebbe resa in questo modo:


<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width">
    <title>Hello World</title>
</head>
<body>
    <h1>Hello, Kinsta</h1>
</body>
</html>

Però sappiamo già come utilizzare i layout, quindi convertiamoli in qualcosa di accessibile a livello globale:

---
import Base from '../layouts/Base.astro';
---

<Base>
    <h1>Hello, Kinsta</h1>
</Base>

Ecco, così è molto più pulito.

Parleremo in dettaglio del routing in Astro più avanti in questo articolo, ma per ora passiamo alle cose più divertenti: la costruzione e la personalizzazione dei siti.

Personalizzare ed estendere Astro

È arrivato il momento di personalizzare un sito creato con Astro! Utilizzeremo le collezioni di Markdown, il routing, la gestione delle immagini e l’integrazione con React per costruire e personalizzare il nostro sito statico.

Collezioni di Markdown

Con la versione 2.0, Astro ha introdotto una soluzione migliore per mantenere i contenuti Markdown rispetto al passato. Grazie alle collezioni, possiamo essere sicuri che tutti i dati di frontmatter siano inclusi e abbiano il corretto tipo di associazione.

Di recente, con la versione 2.5, è stata aggiunta la possibilità di gestire anche i file JSON e YAML come collezioni.

Pronti a sporcarvi le mani?

Per prima cosa, mettete tutti gli articoli Markdown nella cartella src/content/collection_name. Per questo progetto creeremo una collezione blog, quindi nella nostra dimostrazione la cartella sarà src/content/blog.

Ora è il momento di definire nel nostro file src/content/config.ts tutti i campi frontmatter necessari. Il nostro blog avrà bisogno dei seguenti elementi:

  • title (string)
  • tags (array)
  • publishDate (tim)
  • image (string, opzionale)

Ecco come appare il tutto messo insieme:

import { z, defineCollection } from 'astro:content';

const blogCollection = defineCollection({ 
    schema: z.object({
        title: z.string(),
        tags: z.array(z.string()),
        image: z.string().optional(),
        publishDate: z.date(),
    }),
});

export const collections = {
    'blog': blogCollection,
};

Ecco cosa contiene il nostro file di Markdown article-about-astro.md:

---
title: Article about Astro
tags: [tag1, tag3]
publishDate: 2023-03-01
---
## Tamen risit

Lorem *markdownum flumina*, laceraret quodcumque Pachyne, **alter** enim
cadavera choro.

È vero, non c’è niente di speciale nel nostro file di Markdown. Ma c’è una magia nascosta che si manifesta se facciamo un errore di battitura.

Supponiamo, ad esempio, che invece di digitare publishDate, avessimo accidentalmente digitato publishData. Nel caso di un errore di battitura come questo, Astro darà un errore:

blog → article-about-astro.md frontmatter does not match collection schema.
  "publishDate" is required.

Incredibile, vero? Questa funzione ci aiuta a trovare gli errori relativi al frontmatter in pochi secondi.

L’ultima cosa che dobbiamo aggiungere è una pagina che mostri i nostri dati. Creiamo un file in src/page/blog/[slug].astro con il seguente codice:

---
import Base from '../../layouts/Base.astro';
import { getCollection } from 'astro:content';
export async function getStaticPaths() {
    const blogEntries = await getCollection('blog');
    return blogEntries.map(entry => ({
        params: { slug: entry.slug }, props: { entry },
  }));
}
const { entry } = Astro.props;
const { Content } = await entry.render();
---
<Base>
    <h1>{entry.data.title} </h1>
    <Content />
</Base>

Grazie a getStaticPaths, Astro creerà tutte le pagine statiche per ogni post della collezione blog.

L’unica cosa che ci manca ora è un elenco di tutti i nostri articoli:

---
import Base from '../../layouts/Base.astro';

import { getCollection } from 'astro:content';
const blogEntries = await getCollection('blog');
---
<Base>
<ul>
    {blogEntries.map(item => <li> <strong><a href={'/blog/' + item.slug}>{item.data.title}</a></strong></li>)}
</ul>
</Base>

Tutto questo è molto semplice grazie alle collezioni.

Ora creiamo una collezione di tipi di dati. Per prima cosa, dobbiamo aprire nuovamente il file src/content/config.ts e aggiungere una nuova collezione di dati:

import { z, defineCollection, referenece } from 'astro:content';

const blogCollection = defineCollection({ 
	type: 'content',
    schema: z.object({
        title: z.string(),
        tags: z.array(z.string()),
        image: z.string().optional(),
        publishDate: z.date(),
	    author: reference('authors')
    }),
});

const authorsCollection = defineCollection({ 
	type: 'data',
    schema: z.object({
        fullName: z.string(),
        country: z.string()
    }),
});

export const collections = {
    'blog': blogCollection,
'authors': authorsCollection,
};

Oltre a creare una nuova collezione, abbiamo anche aggiunto il riferimento all’autore nella blogCollection.

È ora di creare un nuovo autore. Dobbiamo creare un file chiamato maciek-palmowski.json nella cartella content/authors.json:

{
    "fullName": "Maciek Palmowski",
    "country": "Poland"
}

L’ultima cosa da fare è inserire questi dati nel nostro Post. Per farlo, dovremo utilizzare getEntry:

---
import Base from '../../layouts/Base.astro';
import { getCollection, getEntry } from 'astro:content';
export async function getStaticPaths() {
  const blogEntries = await getCollection('blog');
  return blogEntries.map(entry => ({
    params: { slug: entry.slug }, props: { entry },
  }));
}
const { entry } = Astro.props;
const author = await getEntry(entry.data.author);
const { Content } = await entry.render();
---
<Base>
<h1>{entry.data.title}</h1>
<h2>Author: {author.data.fullName}</h2>
<Content />
</Base>

Routing

Astro ha due diverse modalità di routing. La prima, il routing statico (basato sui file), l’abbiamo già conosciuta quando abbiamo parlato delle pagine.

Ora ci concentreremo sul routing dinamico.

Utilizzando i parametri di instradamento dinamico, potete istruire un file di pagina di Astro in modo da automatizzare la creazione di più pagine con la stessa struttura. Questo è utile quando si hanno molte pagine di un tipo particolare (pensate alle biografie degli autori, ai profili degli utenti, agli articoli della documentazione e così via).

Nel prossimo esempio, lavoreremo sulla generazione di pagine bio per i nostri autori.

Nella modalità di output statico predefinita di Astro, queste pagine vengono generate al momento della build, il che significa che dovrete prestabilire l’elenco degli autori che riceveranno un file corrispondente. In modalità dinamica, invece, le pagine vengono generate su richiesta per ogni percorso.

Se volete passare una variabile come il nome del file, racchiudetela tra parentesi:

pages/blog/[slug].astro -> blog/test, blog/about-me 

Approfondiamo l’argomento utilizzando il codice del nostro file src/page/blog/[slug]:

---
import Base from '../../layouts/Base.astro';
import { getCollection } from 'astro:content';
export async function getStaticPaths() {
    const blogEntries = await getCollection('blog');
    return blogEntries.map(entry => ({
        params: { slug: entry.slug }, props: { entry },
  }));
}
const { entry } = Astro.props;
const { Content } = await entry.render();
---
<Base>
    <h1>{entry.data.title}</h1>
    <Content />
</Base>

La route getStaticPaths è responsabile della generazione di tutte le pagine statiche. Restituisce due oggetti:

  • params: utilizzati per riempire le parentesi nei nostri URL
  • props: tutti i valori che stiamo passando alla pagina

E con questo, la generazione della pagina è sistemata.

Gestione delle immagini

Non possiamo parlare di siti web performanti senza parlare dei moderni formati immagine, dei metodi di ridimensionamento e del lazy loading.

Astro ci aiuta anche in questo caso. Grazie al pacchetto @astrojs/image, possiamo introdurre tutto questo in pochi minuti.

Dopo aver installato il pacchetto, avremo accesso a due componenti: Image e Picture.

Il componente Image viene utilizzato per creare un tag <img /> ottimizzato. Ecco un esempio:

---
import { Image } from '@astrojs/image/components';
import heroImage from '../assets/hero.png';
---

<Image src={heroImage} format="avif" alt="descriptive text" />
<Image src={heroImage} width={300} alt="descriptive text" />
<Image src={heroImage} width={300} height={600} alt="descriptive text" />

Allo stesso modo, il componente Picture crea un componente <picture/> ottimizzato:

---
import { Picture } from '@astrojs/image/components';
import hero from '../assets/hero.png';
---
<Picture src={hero} widths={[200, 400, 800]} sizes="(max-width: 800px) 100vw, 800px" alt="descriptive text" />

SSG vs SSR

Di default, Astro è un generatore di siti statici. Ciò significa che tutti i contenuti vengono convertiti in pagine HTML statiche.

Sebbene questo sia un approccio perfetto da molti punti di vista (soprattutto per quanto riguarda la velocità), a volte potremmo preferire un approccio più dinamico. Ad esempio, se volete una pagina profilo separata per ogni utente o se avete migliaia di articoli sul sito, la restituzione di tutto ogni volta sarebbe troppo dispendiosa in termini di tempo.

Fortunatamente, Astro può funzionare anche come framework completamente renderizzato sul lato server o in una modalità ibrida tra i due.

Per abilitare l’SSR a livello di sito, dobbiamo aggiungere il seguente codice al file astro.config.mjs:

import { defineConfig } from 'astro/config';

export default defineConfig({
    output: 'server'
});

Questo è l’approccio standard.

Con l’approccio ibrido, di default, tutto viene generato dinamicamente, tranne le pagine con l’aggiunta di export const prerender = true.

Con Astro 2.5, c’è anche la possibilità di impostare il rendering statico come predefinito e di selezionare manualmente i percorsi dinamici.

Grazie a questi, possiamo, ad esempio, creare un sito web completamente generato staticamente con pagine di login e profilo dinamiche. Bello, vero?

Per saperne di più, consultate la documentazione ufficiale.

Integrazione di altri framework JavaScript

Un’altra caratteristica sorprendente di Astro vi permette di portare con voi il vostro framework preferito e utilizzarlo con Astro. Potete combinare Astro con React, Preact, Svelte, Vue, Solid o Alpine (per tutte le integrazioni, consultate la documentazione “Add Integrations” di Astro).

Supponiamo di voler utilizzare React. Per prima cosa, dobbiamo installare l’integrazione eseguendo il comando qui sotto in npm:

npx astro add react

Ora che React è stato integrato, possiamo creare un componente React. Nel nostro caso, si tratta del componente contatore in src/components/ReactCounter.tsx:

import { useState } from 'react';

/** A counter written with React */
export function Counter({ children }) {
    const [count, setCount] = useState(0);
    const add = () => setCount((i) => i + 1);
    const subtract = () => setCount((i) => i - 1);

    return (
        <>
            <div className="counter">
                <button onClick={subtract}>-</button>
                <pre>{count}</pre>
                <button onClick={add}>+</button>
                </div>
            <div className="counter-message">{children}</div>
        </>
    );
}

Infine, dobbiamo inserire il contatore nella nostra pagina con il seguente codice:

---
import * as react from '../components/ReactCounter';
---
<main>
    <react.Counter client:visible />
</main>

Et voilà: il componente React è stato integrato perfettamente nel sito.

Come distribuire Astro su Kinsta

Ora è il momento di portare il nostro sito Astro sul web. Kinsta è l’host perfetto per una distribuzione rapida e semplice.

Iniziate creando un repo GitHub per i file del vostro sito. Se non siete pronti a utilizzare i vostri file, potete clonare questo template di avvio di Astro creato dal nostro team.

Una parte del repo GitHub per il sito di avvio Astro di Kinsta, che mostra un'immagine del logo di Astro
Repository GitHub del template di avvio di Astro di Kinsta

Quando la repo è pronta, accedete a MyKinsta, selezionate Applicazioni a sinistra e scegliete Applicazione dal menu a tendina viola Aggiungi servizio.

La dashboard di MyKinsta si è aperta alla sezione
Aggiungere un’applicazione in MyKinsta

L’ultimo passo consiste nel fornire a Kinsta i dettagli della build e della distribuzione.

La maggior parte delle domande che vi verranno poste, come il nome del processo e il metodo di pagamento, avranno risposte ovvie o semplici. Se volete, potete lasciare vuoto il campo Comando di avvio; Kinsta assegnerà automaticamente il comando a npm start.

Se non siete sicuri di come rispondere a qualsiasi altra richiesta, consultate la documentazione di Kinsta per avere indicazioni specifiche sul campo ed esempi di implementazione di Astro. Potete anche consultare la guida di Astro per la distribuzione su Kinsta.

Una volta inseriti i dati della build, cliccate sul pulsante Conferma metodo di pagamento per inizializzare la build.

E il gioco è fatto! Ora avete un sito statico up and running creato con il framework Astro.

Una pagina scura con il logo Kinsta in bianco al centro sopra la scritta
La nostra homepage Astro dal vivo

Potete trovare l’URL live e gli altri dati dell’installazione nella sezione Distribuzioni del vostro account MyKinsta.

La schermata
Una distribuzione Astro andata a buon fine

Riepilogo

Grazie ad una struttura chiara, una sintassi semplice e ai componenti globali, la creazione e l’esecuzione di un’applicazione Astro è davvero semplice. La leggerezza e il doppio uso del routing statico e dinamico aumentano notevolmente la reattività del sito, mentre la capacità di interagire con altri framework JavaScript lo rende ancora più interessante per i coder esperti.

Se il vostro obiettivo è quello di creare un sito ricco di contenuti che carichi rapidamente, che garantisca funzionalità modulari e che fornisca una generazione sia statica che dinamica, allora Astro potrebbe essere la scelta giusta.

Potete ospitare gratuitamente il vostro sito web statico sull’Hosting di Applicazioni di Kinsta e, se vi piace, passare al nostro Piano Hobby.

Cosa ne pensate del generatore di siti statici Astro? L’avete già utilizzato per un progetto? Raccontateci la vostra esperienza nella sezione dei commenti qui sotto.

Maciek Palmowski

Maciek è un web developer che lavora in Kinsta come Development Advocate Analyst. Dopo l'orario di lavoro, passa la maggior parte del tempo a programmare, a cercare notizie interessanti per le sue newsletter o a bere caffè.