Simplifique su pila con un generador de sitio estático hecho a medida
Publicado: 2022-03-10Con la llegada del movimiento Jamstack, los sitios servidos de forma estática han vuelto a estar de moda. La mayoría de los desarrolladores que ofrecen HTML estático no crean HTML nativo. Para tener una experiencia de desarrollador sólida, a menudo recurrimos a herramientas llamadas Generadores de sitios estáticos (SSG).
Estas herramientas vienen con muchas funciones que hacen agradable la creación de sitios estáticos a gran escala. Ya sea que proporcionen enlaces simples a API de terceros, como las fuentes de datos de Gatsby, o proporcionen una configuración detallada, como la enorme colección de motores de plantillas de 11ty, hay algo para todos en la generación de sitios estáticos.
Debido a que estas herramientas están diseñadas para diversos casos de uso, deben tener muchas características. Esas características los hacen poderosos. También los hacen bastante complejos y opacos para los nuevos desarrolladores. En este artículo, reduciremos el SSG a sus componentes básicos y crearemos el nuestro propio.
¿Qué es un generador de sitio estático?
En esencia, un generador de sitios estáticos es un programa que realiza una serie de transformaciones en un grupo de archivos para convertirlos en activos estáticos, como HTML. Qué tipo de archivos puede aceptar, cómo los transforma y qué tipos de archivos salen diferencian a los SSG.
Jekyll, uno de los primeros y todavía populares SSG, usa Ruby para procesar plantillas Liquid y archivos de contenido Markdown en HTML.
Gatsby usa React y JSX para transformar componentes y contenido en HTML. Luego va un paso más allá y crea una aplicación de una sola página que se puede servir de forma estática.
11ty procesa HTML desde motores de plantillas como Liquid, Handlebars, Nunjucks o literales de plantilla de JavaScript.
Cada una de estas plataformas tiene características adicionales para hacernos la vida más fácil. Proporcionan temas, construyen canalizaciones, arquitectura de complementos y más. Con cada característica adicional viene más complejidad, más magia y más dependencias. Son características importantes, sin duda, pero no todos los proyectos las necesitan.
Entre estos tres SSG diferentes, podemos ver otro tema común: datos + plantillas = sitio final. Esta parece ser la funcionalidad central de los sitios estáticos del generador. Esta es la funcionalidad en la que basaremos nuestro SSG.
En esencia, un generador de sitios estáticos es un programa que realiza una serie de transformaciones en un grupo de archivos para convertirlos en activos estáticos, como HTML.
“
La pila tecnológica de nuestro nuevo generador de sitios estáticos: Handlebars, Sanity.io y Netlify
Para construir nuestro SSG, necesitaremos un motor de plantillas, una fuente de datos y un host que pueda ejecutar nuestro SSG y construir nuestro sitio. Muchos generadores usan Markdown como fuente de datos, pero ¿qué sucede si damos un paso más y conectamos de forma nativa nuestro SSG a un CMS?
- Fuente de datos: Sanity.io
- Obtención de datos y plantillas: nodo y manillar
- Host e implementación: Netlify.
requisitos previos
- NodeJS instalado
- Cuenta Sanity.io
- Conocimiento de Git
- Conocimientos básicos de línea de comandos.
- Conocimientos básicos de despliegue a servicios como Netlify.
Nota : para seguir, puede encontrar el código en este repositorio en GitHub.
Configuración de la estructura de nuestro documento en HTML
Para comenzar la estructura de nuestro documento, vamos a escribir HTML simple. No hay necesidad de complicar las cosas todavía.
En la estructura de nuestro proyecto, necesitamos crear un lugar para que vivan nuestros archivos fuente. En este caso, crearemos un directorio src
y pondremos nuestro index.html
dentro.
En index.html
, delinearemos el contenido que queremos. Esta será una página acerca de relativamente simple.
<html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Title of the page!</title> </head> <body> <h1>The personal homepage of Bryan Robinson</h1> <p>Some pagraph and rich text content next</p> <h2>Bryan is on the internet</h2> <ul> <li><a href="linkURL">List of links</a></li> </ul> </body> </html>
Mantengamos esto simple. Comenzaremos con un h1
para nuestra página. Seguiremos eso con algunos párrafos de información biográfica, y anclaremos la página con una lista de enlaces para ver más.
Convierta nuestro HTML en una plantilla que acepte datos
Una vez que tengamos nuestra estructura básica, debemos configurar un proceso para combinar esto con cierta cantidad de datos. Para hacer esto, usaremos el motor de plantillas Handlebars.
En esencia, Handlebars toma una cadena similar a HTML, inserta datos a través de reglas definidas en el documento y luego genera una cadena HTML compilada.
Para usar Handlebars, necesitaremos inicializar un paquete.json e instalar el paquete.
Ejecute npm init -y
para crear la estructura de un archivo package.json con algún contenido predeterminado. Una vez que tengamos esto, podemos instalar Handlebars.
npm install handlebars
Nuestro script de compilación será un script de nodo. Este es el script que usaremos localmente para construir, pero también lo que nuestro proveedor de implementación y host usarán para construir nuestro HTML para el sitio en vivo.
Para iniciar nuestro script, crearemos un archivo index.js
y necesitaremos dos paquetes en la parte superior. El primero es Handlebars y el segundo es un módulo predeterminado en Node para acceder al sistema de archivos actual.
const fs = require('fs'); const Handlebars = require('handlebars');
Usaremos el módulo fs
para acceder a nuestro archivo fuente, así como para escribir en un archivo de distribución. Para comenzar nuestra compilación, crearemos una función main
para que nuestro archivo se ejecute cuando se le llame y una función buildHTML
para combinar nuestros datos y marcas.
function buildHTML(filename, data) { const source = fs.readFileSync(filename,'utf8').toString(); const template = Handlebars.compile(source); const output = template(data); return output } async function main(src, dist) { const html = buildHTML(src, { "variableData": "This is variable data"}); fs.writeFile(destination, html, function (err) { if (err) return console.log(err); console.log('index.html created'); }); } main('./src/index.html', './dist/index.html');
La función main()
acepta dos argumentos: la ruta a nuestra plantilla HTML y la ruta en la que queremos que viva nuestro archivo creado. En nuestra función principal, ejecutamos buildHTML
en la ruta de origen de la plantilla con cierta cantidad de datos.
La función de compilación convierte el documento de origen en una cadena y pasa esa cadena a Handlebars. Handlebars compila una plantilla usando esa cadena. Luego pasamos nuestros datos a la plantilla compilada, y Handlebars representa una nueva cadena HTML reemplazando cualquier variable o lógica de plantilla con la salida de datos.
Devolvemos esa cadena a nuestra función main
y usamos el método writeFile
proporcionado por el módulo del sistema de archivos de Node para escribir el nuevo archivo en nuestra ubicación especificada si el directorio existe.
Para evitar un error, agregue un directorio dist
a su proyecto con un archivo .gitkeep
en él. No queremos enviar nuestros archivos compilados (nuestro proceso de compilación lo hará), pero queremos asegurarnos de tener este directorio para nuestro script.
Antes de crear un CMS para administrar esta página, confirmemos que funciona. Para probar, modificaremos nuestro documento HTML para usar los datos que le acabamos de pasar. Usaremos la sintaxis de la variable Handlebars para incluir el contenido de variableData
.
<h1>{{ variableData }}</h1>
Ahora que nuestro HTML tiene una variable, estamos listos para ejecutar nuestro script de nodo.
node index.js
Una vez que finalice el script, deberíamos tener un archivo en /dist/index.html
. Si leemos y abrimos esto en un navegador, veremos nuestro marcado representado, pero también nuestra cadena "Esto es información variable".
Conexión a un CMS
Tenemos una forma de juntar datos con una plantilla, ahora necesitamos una fuente para nuestros datos. Este método funcionará con cualquier fuente de datos que tenga una API. Para esta demostración, usaremos Sanity.io.
Sanity es una fuente de datos API-first que trata el contenido como datos estructurados. Tienen un sistema de administración de contenido de código abierto para que administrar y agregar datos sea más conveniente tanto para los editores como para los desarrolladores. El CMS es lo que a menudo se conoce como un CMS "sin cabeza". En lugar de un sistema de gestión tradicional en el que sus datos están estrechamente vinculados a su presentación, un CMS autónomo crea una capa de datos que puede ser consumida por cualquier interfaz o servicio (y posiblemente muchos al mismo tiempo).
Sanity es un servicio pago, pero tienen un plan "Estándar" que es gratuito y tiene todas las funciones que necesitamos para un sitio como este.
Configuración de cordura
La forma más rápida de ponerse en marcha con un nuevo proyecto de Sanity es utilizar la CLI de Sanity. Comenzaremos instalando eso globalmente.
npm install -g @sanity/cli
La CLI nos da acceso a un grupo de ayudantes para administrar, implementar y crear. Para comenzar, ejecutaremos sanity init
. Esto nos llevará a través de un cuestionario para ayudar a arrancar nuestro Studio (lo que Sanity llama su CMS de código abierto).
Select a Project to Use: Create new project HTML CMS Use the default dataset configuration? Y // this creates a "Production" dataset Project output path: studio // or whatever directory you'd like this to live in Select project template Clean project with no predefined schemas
Este paso creará un nuevo proyecto y conjunto de datos en su cuenta de Sanity, creará una versión local de Studio y vinculará los datos y el CMS por usted. Por defecto, el directorio de studio
se creará en la raíz de nuestro proyecto. En proyectos de mayor escala, es posible que desee configurar esto como un repositorio separado. Para este proyecto, está bien mantener esto unido.
Para ejecutar nuestro Studio localmente, cambiaremos el directorio al directorio de studio
y ejecutaremos sanity start
. Esto ejecutará Studio en localhost:3333
. Cuando inicie sesión, se le presentará una pantalla para informarle que tiene un "esquema vacío". Con eso, es hora de agregar nuestro esquema, que es cómo se estructurarán y editarán nuestros datos.
Crear un esquema de cordura
La forma de crear documentos y campos dentro de Sanity Studio es crear esquemas dentro del archivo schemas/schema.js
.
Para nuestro sitio, crearemos un tipo de esquema denominado "Acerca de los detalles". Nuestro esquema fluirá de nuestro HTML. En general, podríamos hacer que la mayor parte de nuestra página web sea un solo campo de texto enriquecido, pero es una buena práctica estructurar nuestro contenido de forma separada. Esto proporciona una mayor flexibilidad en la forma en que podríamos querer utilizar estos datos en el futuro.
Para nuestra página web, queremos un conjunto de datos que incluya lo siguiente:
- Título
- Nombre completo
- Biografía (con edición de texto enriquecido)
- Una lista de sitios web con un nombre y URL.
Para definir esto en nuestro esquema, creamos un objeto para nuestro documento y definimos sus campos. Una lista anotada de nuestro contenido con su type
de campo:
- Título: cadena
- Nombre completo: cadena
- Biografía: conjunto de "bloques"
- Lista de sitios web: matriz de objetos con campos de cadena de nombre y URL.
types: schemaTypes.concat([ /* Your types here! */ { title: "About Details", name: "about", type: "document", fields: [ { name: 'title', type: 'string' }, { name: 'fullName', title: 'Full Name', type: 'string' }, { name: 'bio', title: 'Biography', name: 'content', type: 'array', of: [ { type: 'block' } ] }, { name: 'externalLinks', title: 'Social media and external links', type: 'array', of: [ { type: 'object', fields: [ { name: 'text', title: 'Link text', type: 'string' }, { name: 'href', title: 'Link url', type: 'string' } ] } ] } ] } ])
Agregue esto a sus tipos de esquema, guárdelo y su Studio volverá a compilar y le presentará sus primeros documentos. Desde aquí, agregaremos nuestro contenido al CMS creando un nuevo documento y completando la información.
Estructurando su contenido de una manera reutilizable
En este punto, es posible que se pregunte por qué tenemos un "nombre completo" y un "título". Esto se debe a que queremos que nuestro contenido tenga el potencial de ser multipropósito. Al incluir un campo de nombre en lugar de incluir el nombre solo en el título, le damos más uso a esos datos. Luego, podemos usar la información en este CMS para impulsar también una página de currículum o un PDF. El campo de biografía podría usarse programáticamente en otros sistemas o sitios web. Esto nos permite tener una única fuente de verdad para gran parte de este contenido en lugar de estar dictado por el caso de uso directo de este sitio en particular.
Introduciendo nuestros datos en nuestro proyecto
Ahora que hemos hecho que nuestros datos estén disponibles a través de una API, incorporémoslos a nuestro proyecto.
Instalar y configurar el cliente JavaScript de Sanity
Lo primero, necesitamos acceso a los datos en Node. Podemos usar el cliente JavaScript de Sanity para forjar esa conexión.
npm install @sanity/client
Esto buscará e instalará el SDK de JavaScript. Desde aquí, debemos configurarlo para obtener datos del proyecto que configuramos anteriormente. Para hacerlo, configuraremos un script de utilidad en /utils/SanityClient.js
. Proporcionamos el SDK con nuestro ID de proyecto y el nombre del conjunto de datos, y estamos listos para usarlo en nuestro script principal.
const sanityClient = require('@sanity/client'); const client = sanityClient({ projectId: '4fs6x5jg', dataset: 'production', useCdn: true }) module.exports = client;
Obtener nuestros datos con GROQ
De vuelta en nuestro archivo index.js
, crearemos una nueva función para obtener nuestros datos. Para ello, utilizaremos el lenguaje de consulta nativo de Sanity, el GROQ de código abierto.
Crearemos la consulta en una variable y luego usaremos el cliente que configuramos para obtener los datos en función de la consulta. En este caso, construimos un objeto con una propiedad llamada about
. En este objeto, queremos devolver los datos de nuestro documento específico. Para ello, consultamos en función del _id
del documento que se genera automáticamente cuando creamos nuestro documento.
Para encontrar el _id
del documento, navegamos hasta el documento en Studio y lo copiamos desde la URL o pasamos al modo "Inspeccionar" para ver todos los datos del documento. Para ingresar a Inspeccionar, haga clic en el menú "kabob" en la esquina superior derecha o use el atajo Ctrl + Alt + I. Esta vista enumerará todos los datos de este documento, incluido nuestro _id
. Sanity devolverá una serie de objetos de documento, por lo que, para simplificar, devolveremos la entrada 0th
.
Luego pasamos la consulta al método de fetch
de nuestro cliente Sanity y devolverá un objeto JSON de todos los datos en nuestro documento. En esta demostración, devolver todos los datos no es gran cosa. Para implementaciones más grandes, GROQ permite una "proyección" opcional para devolver solo los campos explícitos que desea.
const client = require('./utils/SanityClient') // at the top of the file // ... async function getSanityData() { const query = `{ "about": *[_id == 'YOUR-ID-HERE'][0] }` let data = await client.fetch(query); }
Convertir el campo de texto enriquecido a HTML
Antes de que podamos devolver los datos, debemos hacer una transformación en nuestro campo de texto enriquecido. Mientras que muchos CMS usan editores de texto enriquecido que devuelven HTML directamente, Sanity usa una especificación de código abierto llamada Portable Text. Portable Text devuelve una matriz de objetos (piense en el texto enriquecido como una lista de párrafos y otros bloques multimedia) con todos los datos sobre el estilo y las propiedades del texto enriquecido, como enlaces, notas al pie y otras anotaciones. Esto permite que su texto se mueva y se use en sistemas que no admiten HTML, como asistentes de voz y aplicaciones nativas.
Para nuestro caso de uso, significa que necesitamos transformar el objeto en HTML. Hay módulos NPM que se pueden usar para convertir texto portátil en varios usos. En nuestro caso, usaremos un paquete llamado block-content-to-html.
npm install @sanity/block-content-to-html
Este paquete generará todo el marcado predeterminado del editor de texto enriquecido. Cada tipo de estilo se puede anular para ajustarse al marcado que necesite para su caso de uso. En este caso, dejaremos que el paquete haga el trabajo por nosotros.
const blocksToHtml = require('@sanity/block-content-to-html'); // Added to the top async function getSanityData() { const query = `{ "about": *[_type == 'about'][0] }` let data = await client.fetch(query); data.about.content = blocksToHtml({ blocks: data.about.content }) return await data }
Uso del contenido de Sanity.io en los manillares
Ahora que los datos están en una forma en la que podemos usarlos, pasaremos esto a nuestra función buildHTML
como argumento de datos.
async function main(src, dist) { const data = await getSanityData(); const html = buildHTML(src, data) fs.writeFile(dist, html, function (err) { if (err) return console.log(err); console.log('index.html created'); }); }
Ahora, podemos cambiar nuestro HTML para usar los nuevos datos. Usaremos más llamadas a variables en nuestra plantilla para extraer la mayoría de nuestros datos.
Para representar nuestra variable de content
de texto enriquecido, necesitaremos agregar una capa adicional de llaves a nuestra variable. Esto le indicará a Handlebars que represente el HTML en lugar de mostrarlo como una cadena.
Para nuestra matriz externalLinks
, necesitaremos usar la funcionalidad de bucle integrada de Handlebars para mostrar todos los enlaces que agregamos a nuestro Studio.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{{ about.title }}</title> </head> <body> <h1>The personal homepage of {{ about.fullName }}</h1> {{{ about.content }}} <h2>Bryan is on the internet</h2> <ul> {{#each about.externalLinks }} <li><a href="{{ this.href }}">{{ this.text }}</a></li> {{/each}} </ul> </body> </html>
Configuración de la implementación
Hagamos esto en vivo. Necesitamos dos componentes para que esto funcione. Primero, queremos un host estático que construya nuestros archivos por nosotros. A continuación, debemos activar una nueva compilación de nuestro sitio cuando se cambia el contenido en nuestro CMS.
Implementación en Netlify
Para el alojamiento, usaremos Netlify. Netlify es un host de sitio estático. Sirve activos estáticos, pero tiene características adicionales que harán que nuestro sitio funcione sin problemas. Tienen una infraestructura de implementación integrada que puede ejecutar nuestro script de nodo, webhooks para activar compilaciones y una CDN distribuida globalmente para garantizar que nuestra página HTML se sirva rápidamente.
Netlify puede ver nuestro repositorio en GitHub y crear una compilación basada en un comando que podemos agregar en su tablero.
Primero, necesitaremos enviar este código a GitHub. Luego, en el Tablero de Netlify, necesitamos conectar el nuevo repositorio a un nuevo sitio en Netlify.
Una vez que esté conectado, debemos decirle a Netlify cómo construir nuestro proyecto. En el tablero, nos dirigiremos a Configuración > Generar e implementar > Configuración de compilación. En esta área, debemos cambiar nuestro "Comando de compilación" a "node index.js" y nuestro "Directorio de publicación" a "./dist".
Cuando Netlify construye nuestro sitio, ejecutará nuestro comando y luego verificará la carpeta que enumeramos en busca de contenido y publicará el contenido dentro.
Configuración de un webhook
También debemos decirle a Netlify que publique una nueva versión cuando alguien actualice el contenido. Para hacer eso, configuraremos un Webhook para notificar a Netlify que necesitamos reconstruir el sitio. Un webhook es una URL a la que se puede acceder mediante programación mediante un servicio diferente (como Sanity) para crear una acción en el servicio de origen (en este caso, Netlify).
Podemos configurar un "enlace de compilación" específico en nuestro panel de control de Netlify en Configuración > Generar e implementar > Enganches de compilación. Agregue un gancho, asígnele un nombre y guárdelo. Esto proporcionará una URL que se puede usar para activar de forma remota una compilación en Netlify.
A continuación, debemos indicarle a Sanity que realice una solicitud a esta URL cuando publique los cambios.
Podemos usar la CLI de Sanity para lograr esto. Dentro de nuestro directorio /studio
, podemos ejecutar sanity hook create
para conectarnos. El comando le pedirá un nombre, un conjunto de datos y una URL. El nombre puede ser el que desee, el conjunto de datos debe ser production
para nuestro producto y la URL debe ser la URL que proporcionó Netlify.
Ahora, cada vez que publiquemos contenido en Studio, nuestro sitio web se actualizará automáticamente. No es necesario marco.
- El código se puede encontrar en este repositorio de GitHub →
Próximos pasos
Este es un ejemplo muy pequeño de lo que puede hacer cuando crea sus propias herramientas. Si bien los SSG con más funciones completas pueden ser lo que necesita para la mayoría de los proyectos, la creación de su propio mini-SSG puede ayudarlo a comprender más sobre lo que sucede en el generador de su elección.
- Este sitio publica solo una página, pero con un poco más en nuestro script de compilación, podríamos publicar más páginas. Incluso podría publicar una entrada de blog.
- La "experiencia del desarrollador" falta un poco en el repositorio. Podríamos ejecutar nuestro script de Node en cualquier archivo guardado implementando un paquete como Nodemon o agregando "recarga en caliente" con algo como BrowserSync.
- Los datos que viven en Sanity pueden impulsar múltiples sitios y servicios. Puede crear un sitio de currículum que use esto y publique un PDF en lugar de una página web.
- Puede agregar CSS y hacer que parezca un sitio real.