Renderizado dinámico con Rendertron

Jueves, 31 de enero del 2019

Muchos frameworks de frontend dependen de JavaScript para mostrar contenido, por lo que es posible que Google tarde un poco en indexar tu contenido o en actualizar el contenido indexado.

Este año en Google I/O hemos hablado sobre una posible solución: el renderizado dinámico. Hay muchas formas de implementarla, pero la que tratamos en esta entrada de blog es mediante Rendertron, un proyecto de código abierto disponible en una versión sin interfaz gráfica de Chromium.

¿En qué sitios podría ser útil el renderizado dinámico?

No todos los robots de los buscadores y las redes sociales que visitan tu sitio web pueden ejecutar JavaScript. Por ejemplo, el robot de Google puede tardar un tiempo en ejecutar el código JavaScript y tiene algunas limitaciones.

Resulta útil emplear renderizado dinámico con contenido que cambia con frecuencia y necesita JavaScript para mostrarse. La experiencia de usuario de tu sitio (especialmente el tiempo que tarda el primer renderizado significativo) puede mejorarse si se utiliza un renderizado híbrido (por ejemplo, Angular Universal).

¿Cómo funciona el renderizado dinámico?

Cómo funciona el renderizado dinámico

Renderizado dinámico significa cambiar entre renderizar el contenido en el cliente y renderizarlo previamente según el user-agent.

Deberás tener un procesador para poder ejecutar JavaScript y generar archivos HTML estáticos. Rendertron es un proyecto de código abierto que renderiza mediante una versión de Chromium sin interfaz gráfica. Las aplicaciones de página única suelen cargar datos en segundo plano o retrasar su carga para renderizar su contenido. Rendertron tiene mecanismos que determinan si se ha completado el renderizado de un sitio web y espera hasta que todas las solicitudes de red hayan finalizado y no haya ningún proceso pendiente.

Esta entrada se divide en los siguientes temas:

  1. Aplicación web de muestra
  2. Configuración de un servidor express.js básico que sirva la aplicación web
  3. Instalación y configuración de Rendertron como middleware para aplicar renderizado dinámico

Aplicación web de muestra

La aplicación web Kitten Corner usa JavaScript para cargar varias imágenes de gatos de una API y las muestra en una cuadrícula.

Imágenes de gatitos adorables en cuadrícula y un botón para que aparezcan más: ¡esta aplicación lo tiene todo!

Así se representa el JavaScript:

const apiUrl = 'https://api.thecatapi.com/v1/images/search?limit=50';
   const tpl = document.querySelector('template').content;
   const container = document.querySelector('ul');
   function init () {
     fetch(apiUrl)
     .then(response => response.json())
     .then(cats => {
       container.innerHTML = '';
       cats
         .map(cat => { const li = document.importNode(tpl, true); li.querySelector('img').src = cat.url; return li;
         }).forEach(li => container.appendChild(li));
     })
   }
   init();
   document.querySelector('button').addEventListener('click', init);

La aplicación web usa un tipo de JavaScript reciente (ES6) que todavía no es compatible con el robot de Google. Con la prueba de optimización para móviles se puede comprobar si el robot de Google puede ver el contenido:

En la prueba de optimización para móviles se indica que la página está optimizada para estos dispositivos, pero en la captura de pantalla no aparece ningún gato. Aunque sí aparecen el título y el botón, no hay ninguna imagen de gatos.

Si bien este problema es fácil de solucionar, es útil saber cómo se configura el renderizado dinámico, puesto que permitirá que el robot de Google vea las imágenes de gatos sin que tengas que editar el código de la aplicación web.

Configurar el servidor

Para servir la aplicación web, utilizaremos express, una biblioteca de node.js, para compilar servidores web.

El código del servidor tiene una estructura similar a la que aparece a continuación. Si quieres, consulta el código fuente completo del proyecto aquí.

const express = require('express');
const app = express();
const DIST_FOLDER = process.cwd() + '/docs';
const PORT = process.env.PORT || 8080;
// Serve static assets (images, css, etc.)
app.get('*.*', express.static(DIST_FOLDER));
// Point all other URLs to index.html for our single page app
app.get('*', (req, res) => {
  res.sendFile(DIST_FOLDER + '/index.html');
});
// Start Express Server
app.listen(PORT, () => {
  console.log(`Node Express server listening on https://localhost:${PORT} from ${DIST_FOLDER}`);
});

Puedes probar este ejemplo: si usas un navegador moderno, deberías ver muchas imágenes de gatos. Para ejecutar el proyecto desde el ordenador, deberás tener node.js para ejecutar estos comandos:

npm install --save express rendertron-middleware
node server.js

A continuación, dirige tu navegador a https://localhost:8080. Ahora es el momento de configurar el renderizado dinámico.

Implementar una instancia de Rendertron

Rendertron ejecuta un servidor que toma una URL y devuelve un archivo HTML estático de la URL mediante el navegador Chromium sin interfaz gráfica. Seguiremos las recomendaciones del proyecto Rendertron y usaremos Google Cloud Platform.

Formulario para crear un proyecto de Google Cloud Platform.

Puedes empezar con el nivel de uso disponible sin pago, pero debes tener en cuenta que, si te decantas por esta opción, usar aplicaciones de producción puede conllevar costes tal como se indica en los precios de Google Cloud Platform.

  1. Crea un proyecto en la consola de Google Cloud. Anota el "ID del proyecto" que aparece debajo del campo de entrada.
  2. Instala el SDK de Google Cloud tal como se describe en la documentación e inicia sesión.
  3. Clona el repositorio de Rendertron desde GitHub con:
    git clone https://github.com/GoogleChrome/rendertron.git
    cd rendertron
  4. Ejecuta en tu ordenador estos comandos para instalar dependencias y compilar Rendertron:
    npm install && npm run build
  5. Para habilitar la caché de Rendertron, crea un archivo llamado config.json en el directorio de Rendertron con el siguiente elemento:
    { "datastoreCache": true }
  6. Ejecuta el siguiente comando desde el directorio de Rendertron, pero cambiando YOUR_PROJECT_ID por el ID del proyecto del paso 1.
    gcloud app deploy app.yaml --project YOUR_PROJECT_ID
  7. Selecciona una zona geográfica, confirma la implementación y espera hasta que finalice.
  8. Escribe la URL YOUR_PROJECT_ID.appspot.com. A continuación, debería aparecer la interfaz de Rendertron con un campo de entrada y algunos botones.
Interfaz de Rendertron después de la implementación en Google Cloud Platform

Si ves la interfaz web de Rendertron, significa que has implementado correctamente tu propia instancia de Rendertron. Anota la URL de tu proyecto (YOUR_PROJECT_ID.appspot.com), ya que la necesitarás en la siguiente fase del proceso.

Añadir Rendertron al servidor

El servidor web utiliza express.js y Rendertron tiene un middleware express.js. Ejecuta el siguiente comando en el directorio del archivo server.js:

npm install --save rendertron-middleware

Este comando instala el middleware de Rendertron desde npm para que podamos añadirlo al servidor:

const express = require('express');
const app = express();
const rendertron = require('rendertron-middleware');

Configurar la lista de robots

Rendertron usa el encabezado HTTP user-agent para determinar si una solicitud procede de un robot o del navegador de un usuario y lo compara con una lista actualizada de user-agents de robots. Dado que el robot de Google puede ejecutar JavaScript, la lista no incluye el robot de Google de manera predeterminada. Para que Rendertron también procese las solicitudes del robot de Google, añádelo a la lista de user-agents:

const BOTS = rendertron.botUserAgents.concat('googlebot');
const BOT_UA_PATTERN = new RegExp(BOTS.join('|'), 'i');

Rendertron compara el encabezado user-agent con esta expresión regular más adelante.

Añadir el middleware

Para enviar solicitudes de robots a la instancia de Rendertron, su middleware debe estar incluido en nuestro servidor express.js. El middleware comprueba el user-agent que está haciendo las solicitudes y reenvía las de robots conocidos a la instancia de Rendertron. Añade el siguiente fragmento de código a server.js, cambiando YOUR_PROJECT_ID por el ID de tu proyecto de Google Cloud Platform:

app.use(rendertron.makeMiddleware({
  proxyUrl: 'https://YOUR_PROJECT_ID.appspot.com/render',
  userAgentPattern: BOT_UA_PATTERN
}));

Los robots que solicitan el sitio web de muestra reciben el archivo HTML estático de Rendertron, por lo que no hace falta que ejecuten JavaScript para mostrar el contenido.

Probar la configuración

Para probar si la configuración de Rendertron se ha hecho correctamente, vuelve a ejecutar la prueba de optimización para móviles.

La prueba de optimización para móviles muestra que la página está optimizada para móviles, y en la captura de pantalla ya aparecen todos los gatos que faltaban.

A diferencia de lo que pasaba en la primera prueba, ahora aparecen las imágenes de gatos. En la pestaña HTML se muestra todo el HTML generado por código de JavaScript y se indica que Rendertron ha eliminado la necesidad de ejecutar JavaScript para mostrar el contenido.

Conclusión

Has creado una configuración de renderizado dinámico sin editar la aplicación web. Con estos cambios, puedes servir una versión HTML estática de la aplicación web a los rastreadores.