Angular Scully es un generador de sitios estáticos para aplicaciones Angular. En otras palabras, Scully se encarga de renderizar las páginas de tu app Angular y generar archivos estáticos HTML y CSS que no necesitan Javascript para cargar.
Evidentemente, no podrás reemplazar una app Angular que necesite ejecutar cierta lógica por simples archivos estáticos (también necesitarás JS), pero si te va a ayudar a reducir el tiempo de carga inicial de la página, y/o adecuarla para la indexación SEO.
Volveré a Scully dentro de unas líneas, pero antes déjame ponerte algo de contexto.
Angular vs. páginas estáticas
Este debate no es nuevo.
Típicamente, una app Angular es un archivo HTML prácticamente vacío que enlaza a varios archivos Javascript. Cuando visitas una página de este tipo, tu navegador muestra una página en blanco, se descarga el JS asociado y lo ejecuta. Es entonces, cuando aparece en pantalla tu flamante página creada con Angular.
El problema es que… dependiendo de tu conexión y dispositivo, puedes tardar varios segundos en ver contenido interesante por pantalla.
A nadie le gustan las páginas en blanco.
Esta situación, contrasta con las páginas estáticas tradicionales. Cuando visitas una página estática, típicamente el navegador se descarga un archivo HTML que contiene toda la estructura de la página, además de enlazar a archivos CSS y JS que no son imprescindibles para el funcionamiento de la página (animaciones, analytics, …). En este caso el contenido se ve enseguida, mucho antes de descargarse los archivos JS asociados.
Alternativas al pantallazo blanco de Angular
A nadie le gustan las páginas en blanco, está claro, pero no te preocupes, hay mecanismos para evitarlo.
Para empezar, puedes mostrar una animación mientras se carga tu app Angular. Es una solución básica pero que sirve si no necesitas SEO y el tiempo de carga no es crítico, como en un dashboard.
Otra alternativa sería Angular Universal. Es una herramienta de Server Side Rendering (SSR), que renderiza las páginas Angular en un servidor antes de enviarlas al cliente. Tiene 2 claros beneficios:
- SEO: Los robots de indexación no ven una página vacía, si no la página completa tal y como la verías tú
- Menor tiempo de FCP: El tiempo de First Contentful Paint se reduce porque en vez de una página en blanco, se recibe una página ya renderizada, que se muestra mientras se carga toda la app en JS por debajo.
Entonces… ¿es suficiente con Universal?
El problema del SSR es que renderiza la página en el momento en que la solicita el cliente. Esto añade un cierto retraso a la hora de servir la página y además te obliga a tener un servidor en ejecución.
Es decir, yo navego a, pongamos, www.my-angular-ssr-page.com, donde hay un servidor esperando que recibe mi petición, renderiza la página y cuando la tiene lista se la envía a mi navegador. Esto es el SSR.
Si tu contenido cambia de forma continuada, puede tener sentido esta aproximación, pero si tu contenido no se actualiza mucho, hay alternativas más interesantes.
Pre-rendering
Esto va un paso más allá del SSR. El pre-rendering consiste en renderizar las páginas de tu aplicación ANTES de que se realice la petición del cliente. Todas esas páginas ya renderizadas las subes entonces a un host o CDN, para hacerlas accesibles a cualquier persona que quiera acceder a ellas.
Hay muchas estrategias. Puedes hacer pre-rendering como un paso intermedio en tu proceso de deploy, o tener una tarea periódica que actualiza tus páginas de vez en cuando, por ejemplo. Eso ya depende de tus necesidades, pero lo importante son las ventajas que ofrece respecto a SSR:
- Más rápido: cuando el cliente navega, la página se sirve de inmediato (ya renderizada), como una página estática.
- «Sin» servidor: no necesitas un servidor en marcha todo el tiempo que reciba las visitas de tus usuarios y renderice lo mismo una y otra vez.
El pre-rendering, de hecho, es una pieza clave en las arquitecturas JAMStack que tan de moda se están poniendo últimamente. Es la arquitectura que tienen en mente los generadores de sitios estáticos como Gatsby.js (para React), Nuxt (para Vue) y ahora Scully (para Angular, claro).
Scully… ¿si o no?
Decía al principio del artículo que quizás no necesitas Scully. No es que esté en contra del pre-rendering de páginas Angular, ni mucho menos.
Pero…
¿y si te dijera que puedes conseguir lo mismo, de forma más simple, con Angular Universal?
Angular Universal, además de SSR, siempre ha soportado pre-rendering. El problema es que en sus orígenes era complejo de utilizar. Pero eso ha pasado a la historia.
Hacer pre-rendering con Angular Universal es ahora coser y cantar 🙂
Fíjate, para una aplicación con rutas estáticas, sería tan simple como:
- Añadir Angular Universal al proyecto Angular, mediante schematics
ng add @nguniversal/express-engine
-
Ejecutar la función de pre-render que ha añadido el schematics anterior:
npm run prerender
Ya está. Tras estos dos pasos, la CLI de Angular habría compilado tu aplicación, generado un árbol con los links desde la página raíz, y renderizado tu app Angular para cada una de esas rutas.
En cuestión de segundos, tendrías una carpeta con todas las páginas renderizadas como HTML estático, listas para subir a cualquier hosting o CDN.
Usar Scully tampoco tiene complicaciones
Scully también utiliza schematics, así que usarlo es igual de simple:
- Añadir Scully a un proyecto Angular
ng add @scullyio/init
-
Compilar el proyecto
ng build
-
Y renderizarlo con Scully (usamos
scanRoutes
para que encuentre rutas estáticas)
npm run scully -- --scanRoutes
Scully vs Universal
Decantarse entre uno u otro es una cuestión de necesidades.
Los dos funcionan de forma muy simple. En caso de tener rutas dinámicas, a ambos puedes pasarles un archivo con dichas rutas, así que tampoco aquí hay diferencias.
Sin embargo, a nivel de implementación son muy diferentes. El pre-render de Universal básicamente añade un servidor en Node para renderizar la página, mientras que Scully utiliza Puppeteer para lanzar una versión headless de Chrome, y así obtener las versiones renderizadas de cada página.
Además, Scully tiene muchas más funcionalidades que el pre-rendering de Universal (que puedes necesitar, o no).
Scully: pros
- Permite usar archivos markdown para generar el contenido de las páginas a renderizar, así que puede ser muy util a la hora de crear un blog.
- Se puede extender con plugins, para realizar cosas interesantes como minificar del HTML en el proceso de pre-rendering. Su ecosistema de plugins de momento es pequeño, pero tiempo al tiempo. Además, siempre puedes crearte tus propios plugins.
- Dado que corre en Puppeteer, el entorno de pre-rendering sigue siendo «browser», así que no tienes que preocuparte de si objetos como
document
están definidos o no.
Scully: contras
- Justamente porque corre en Puppeteer, el proceso de pre-rendering es mucho más lento. Si solo renderizas 10 páginas, no notarás la diferencia, pero he visto benchmarks donde al renderizar 1000 páginas, el proceso en Scully es 4 veces mayor que en Universal.
- Además de lento, también es un proceso que consume más recursos. Si quieres añadir pre-rendering como un paso más de tu integración continua, Universal puede ser mejor opción.
- Scully es muy reciente y puedes encontrarte con algún bug que otro. De momento, Universal es una solución más segura para ir a producción.
Reflexiones personales
No quiero que leas este artículo como algo a favor de una u otra opción. Son soluciones distintas para un mismo problema, y las dos me parecen muy interesantes. Mi objetivo es simplemente que seas consciente de las ventajas y desventajas de cada aproximación, para que entiendas cuál es más adecuada para tu propia situación.
¿Te ha gustado este artículo? No te cortes, déjame un comentario y ayúdame a compartirlo 😉