Cómo Gatsby eludió Next.js

El autor del artículo, cuya traducción publicamos hoy, trabaja como programador en Antler. Esta compañía es un generador global de startups. Hay días de demostración en Antler varias veces al año, que reúne a muchos creadores e inversores de todo el mundo. La situación en torno a COVID-19 obligó a Antler a traducir sus eventos a un formato en línea.



La compañía quería asegurarse de que los visitantes de sus eventos virtuales, sin distraerse con nada y sin quedarse atascados en ningún lado, vieran lo más importante. A saber, las ideas de startups presentadas al público, expresadas como los contenidos de las páginas web. Los días de demostración virtual pueden ser de interés para un público bastante amplio. Algunos miembros de esta audiencia pueden estar participando por primera vez en algo como esto. Por lo tanto, la empresa tuvo que hacer todo de la mejor manera posible y proporcionar una carga de alta velocidad de páginas que representan nuevas empresas. Decidieron que este es el caso cuando una aplicación web progresiva de alto rendimiento (PWA, Progressive Web App) puede ser útil. El problema principal era encontrar la tecnología adecuada para desarrollar PWA.


¿Representación del servidor o generador de sitio estático?


Para comenzar, te presentaré un poco al curso. Todos nuestros proyectos se basan en React y la biblioteca Material-UI. Como resultado, inicialmente decidimos no apartarnos de esta pila de tecnología, lo que nos permitiría garantizar una alta velocidad de desarrollo y hacer que el nuevo proyecto sea compatible con lo que ya tenemos. La diferencia clave entre este nuevo proyecto y nuestras otras aplicaciones React fue que la base de datos para ellos se creó utilizando la aplicación create-react-y que se representaron completamente en el cliente (CSR, Representación del lado del cliente). Esto, en particular, condujo al hecho de que cuando la aplicación se cargó inicialmente, los usuarios se vieron obligados a observar una pantalla en blanco mientras el código JavaScript del proyecto se cargaba, procesaba y ejecutaba.

Necesitábamos un nivel de rendimiento intransigente. Por lo tanto, comenzamos a pensar en usar la representación del lado del servidor (SSR, Representación del lado del servidor) o un generador de sitio estático (SSG, Generador de sitio estático) para que la carga inicial de aplicaciones sea lo más rápida posible.

Nuestros datos se almacenan en Cloud Firestore, y accedemos a ellos utilizando Algolia. Esto nos permite controlar, a nivel de campo de la base de datos, el acceso público a los datos con claves API restringidas. Esto también mejora el rendimiento de la consulta. Por experiencia, sabemos que las consultas de Algolia son más rápidas de lo normal y que el SDK de JavaScript de Firestore comprimido tiene un tamaño de 86 KB . En el caso de Algolia, esto es 7.5 Kb .

Además, queríamos que los datos que proporcionamos a los clientes sean lo más actualizados posible. Esto nos ayudaría a corregir rápidamente datos erróneos que podrían publicarse accidentalmente. Si bien la práctica estándar de SSG prevé la implementación de solicitudes de datos relevantes durante el ensamblaje del proyecto, esperábamos que en nuestra base de datos los datos se escriban con bastante frecuencia. En particular, estamos hablando de la grabación de datos iniciada por administradores que usan la interfaz de firetable, y por iniciativa de los fundadores de los proyectos utilizando el portal web. Esto lleva a un montaje competitivo del proyecto. Además, debido a las características estructurales de nuestra base de datos, cambios menores pueden conducir a nuevas operaciones de ensamblaje de proyectos. Esto hace que nuestra línea de CI / CD sea extremadamente ineficiente. Por lo tanto, necesitábamos que las solicitudes para recibir datos del repositorio se ejecutaran cada vez que un usuario solicita que se cargue una página. Desafortunadamente, esto significaba que nuestra solución no sería un ejemplo de un proyecto SSG "limpio".

Inicialmente, nuestra aplicación fue creada sobre la base de Gatsby, ya que ya usábamos páginas de aterrizaje creadas en Gatsby, y en una de ellas ya se usaba la biblioteca Material-UI. La primera versión del proyecto formó una página que, mientras se cargaban los datos, mostraba un "esqueleto". Al mismo tiempo, la primera pintura contenta (FCP) fue en la región de 1 segundo.


Descargar el "esqueleto" de la página con la posterior carga de datos

La solución resultó ser interesante, pero tenía sus inconvenientes, porque los datos para la salida de la página se descargaron por iniciativa del cliente:

  • Para ver el contenido de la página, los usuarios tendrían que esperar la descarga de esta página y los datos que se muestran en ella, obtenidos a través de 4 solicitudes a Algolia.
  • JS- . , React «» . DOM.
  • . , , .

Como resultado, durante el largo fin de semana, decidí experimentar con la versión SSR del proyecto creado con Next.js. Afortunadamente para mí, la documentación de Material-UI tenía un proyecto de ejemplo para Next.js. Por lo tanto, no necesitaba aprender todo este marco desde cero. Solo tenía que mirar algunas partes del tutorial y la documentación. Convertí la aplicación a un proyecto que se renderizó en el servidor. Cuando un usuario solicitó una carga de página, el servidor ejecutó las solicitudes de datos necesarios para llenar la página. Este paso nos permitió resolver los tres problemas anteriores. Aquí están los resultados de la prueba para dos opciones de aplicación.


Resultados de la búsqueda de aplicaciones con Google PageSpeed ​​Insights . A la izquierda está Gatsby (SSG), a la derecha está Next.js (SSR) ( imagen original )

El FCP para la versión Next.js del proyecto fue aproximadamente 3 veces mayor que para su versión basada en Gatsby. La versión Gatsby del proyecto tenía un índice de velocidad de 3.3 segundos, mientras que la versión Next.js tenía 6.2 segundos. El tiempo hasta el primer byte (TTFB, Tiempo hasta el primer byte) fue de 2.56 segundos cuando se usa Next.js y de 10-20 ms cuando se usa Gatsby.

Cabe señalar que la versión Next.js del sitio se implementó en otro servicio (aquí utilizamos los servicios ZEIT Now y Firebase Hosting; esto también podría afectar el aumento de TTFB). Pero, a pesar de esto, estaba claro que la transferencia de operaciones de carga de datos al servidor hacía que el sitio pareciera más lento, a pesar del hecho de que todos los materiales de la página se cargaron aproximadamente al mismo tiempo. El hecho es que en la versión Next.js del proyecto, el usuario por un tiempo solo ve una página en blanco.


Captura de pantalla que muestra la carga de dos versiones de una aplicación. La descarga no se completó al mismo tiempo. Los registros se sincronizan en el momento en que presiona la tecla Intro.

Todo esto nos brinda una importante lección del campo del desarrollo web: debe brindar a los usuarios comentarios visuales. Un estudio encontró que las aplicaciones que usan pantallas esqueléticas parecen cargarse más rápido.

Este resultado, además, no se corresponde con el estado de ánimo que podría haber captado si leyó artículos sobre desarrollo web en los últimos años. Es decir, estamos hablando del hecho de que no hay nada de malo en usar los recursos del cliente, y que el SSR no es una solución integral para los problemas de rendimiento.

Rendimiento de generación de sitio estático: comparación de Gatsby y Next.js


Si bien los dos marcos en consideración, Gatsby y Next.js, son conocidos, respectivamente, por su capacidad de generar sitios estáticos y representación de servidores, el soporte para SSG se mejoró en Next.js 9.3 , lo que lo convierte en un competidor de Gatsby.

En el momento de escribir este artículo, la capacidad de Next.js para generar sitios estáticos todavía era muy reciente. Ella tenía poco más de un mes. Ella todavía se informa en la primera página del proyecto. Ahora no hay muchas comparaciones de las capacidades de SSG de Gatsby y Next.js (o tal vez aún no haya tales comparaciones). Como resultado, decidí realizar mi propio experimento.

Regresé la versión Gatsby del proyecto al estado en que se descargaron los datos en el cliente, y lo hice para que ambas versiones de la aplicación tengan exactamente el mismo conjunto de características. Es decir, tuve que eliminar de qué son responsables los complementos de Gatsby: funciones de SEO, generación de favicons, manifiesto de PWA. Para comparar exclusivamente paquetes de JavaScript creados por frameworks, no incluí imágenes y otro contenido descargado de fuentes externas en los proyectos. Ambas versiones de la aplicación se implementaron en la plataforma Firebase Hosting. Como referencia, se crearon dos versiones de la aplicación basadas en Gatsby 2.20.9 y Next.js 9.3.4.

Ejecuté Lighthouse en mi computadora 6 veces para cada versión. Los resultados mostraron una ligera ventaja para Gatsby.


Valores promedio obtenidos después de 6 lanzamientos de Lighthouse para cada marco ( imagen original )

En términos de evaluación general del rendimiento, la versión Next.js estaba solo ligeramente por detrás de la versión Gatsby. Lo mismo ocurre con FCP y Speed ​​Index. El Retardo de primer ingreso del primer potencial para la versión Next.js de la aplicación es ligeramente mayor que para la versión Gatsby.

Para comprender mejor lo que está sucediendo, recurrí a la pestaña Red de las herramientas para desarrolladores de Chrome. Al final resultó que, en la versión Next.js del proyecto, el número de fragmentos en los que se divide el código JavaScript es 3 más que en la versión Gatsby (excluyendo los archivos de manifiesto), pero el código comprimido es 20 KB más pequeño. ¿Pueden las solicitudes adicionales necesarias para descargar estos archivos superar los beneficios de un paquete más pequeño que perjudica el rendimiento?


En la versión Gatsby del proyecto, se ejecutan 7 solicitudes para descargar 379 KB de datos. En la versión del proyecto Next.js: 12 solicitudes de descarga de 359 KB de datos ( imagen original )

Si analiza el rendimiento de JavaScript, las herramientas del desarrollador dicen que la versión del proyecto Next.js necesita 300 ms adicionales para la primera representación, y que esta versión pasa más tiempo en la tarea Evaluar secuencia de comandos. En las herramientas del desarrollador, esta tarea incluso se marcó como una "tarea larga".


Análisis del rendimiento de diferentes opciones de proyecto utilizando la pestaña Rendimiento de las herramientas de desarrollador de Chrome ( imagen original )

. Comparé el código del proyecto para averiguar si existen diferencias en su implementación que pudieran afectar el rendimiento. Con la excepción de eliminar el código innecesario y las correcciones asociadas con los tipos faltantes de TypeScript, la única diferencia fue la implementación de un desplazamiento suave de la página cuando se mueve a sus partes individuales. Esta característica fue introducida previamente por un archivogatsby-browser.jsy se movió a un componente importado dinámicamente . Como resultado, este código solo se ejecutaría en un navegador. (Utilizamos el paquete npm de desplazamiento suavey, al importarlo, necesita un objetowindow.) Este problema puede ser el culpable, pero no sé cómo se maneja en Next.js.

Gatsby es más conveniente desde el punto de vista del desarrollador


Al final, decidí optar por la versión Gatsby del proyecto. Además, aquí no tomé en cuenta la ventaja de rendimiento muy pequeña que mostró Gatsby en comparación con el mecanismo Next.js SSG (¿no me aferraré seriamente a la ventaja de 0.6 segundos?). El hecho es que en la versión Gatsby del proyecto muchas funciones PWA ya están implementadas, y no vi el punto de tenerlas implementadas nuevamente en la versión Next.js de la aplicación.

Cuando estaba creando la primera versión de Gatsby del proyecto, pude agregar rápidamente algunas características útiles de PWA al proyecto. Por ejemplo, para agregar a cada página mis propias metaetiquetas necesarias para SEO, solo tenía que leer el manual . Para equipar el proyecto con un manifiesto PWA, solo necesitaba usar el complemento apropiado. Para equipar el proyecto con favicons que admitieran todas las plataformas disponibles (y en este caso todavía hay un desastre terrible ), ni siquiera tuve que hacer nada, ya que el soporte de favicon es parte del complemento responsable del manifiesto. ¡Es muy cómodo!

Implementar las mismas características en la versión Next.js de la aplicación requeriría más trabajo. Tendría que buscar manuales de capacitación, todo tipo de "mejores prácticas". Y el hecho de que tendría éxito, de todos modos, no me daría ninguna ventaja. Después de todo, sin embargo, la versión Next.js del proyecto no difiere en un rendimiento más alto que su versión Gatsby. Esta, además, fue la razón por la que decidí simplemente deshabilitar las características correspondientes de la versión Gatsby del proyecto, comparándola con la versión Next.js. La documentación de Next.js es más concisa que la documentación de Gatsby (quizás el hecho es que Next.js es más pequeña que Gatsby) Realmente me gusta el tutorial gamified Next.js. Pero la documentación más extensa de Gatsby es más valiosa con el desarrollo real de PWA, aunque a primera vista parece enorme.


Documentación de Gatsby

Es cierto, no puedo guardar silencio sobre las fortalezas de Next.js:

  • Gracias al tutorial y la brevedad de la documentación de Next.js, parece que este marco se puede aprender más rápido que Gatsby.
  • El sistema de carga de datos utilizado en Next.js se basa en funciones asincrónicas y la API Fetch. Como resultado, al desarrollar Next.js, el desarrollador no tiene la sensación de que necesita aprender GraphQL para aprovechar al máximo las capacidades del marco.
  • Next.js TypeScript, Gatsby , ( ). Next.js , , , .

Gracias al hecho de que Next.js ha mejorado la compatibilidad con SSG, este marco se ha convertido en una herramienta poderosa que permite, a nivel de cada página individual, elegir el método para trabajar con ella. Puede ser SSR, SSG o CSR.

De hecho, si pudiera generar esta aplicación en una forma completamente estática, Next.js me convendría mejor, ya que podría usar el Algolia JS-API estándar y podría mantener el código para cargar datos en el mismo archivo que y código de componente. Dado que Algolia no tiene una API GraphQL incorporada y no hay un complemento de Gatsby para Algolia, implementar dicho mecanismo en Gatsby requeriría agregar este código a un nuevo archivo . Y esto va en contra de la forma declarativa intuitiva de describir páginas.

Sobre formas adicionales de mejorar el rendimiento del proyecto


Después de haber resuelto el problema de elegir un marco, se puede observar que hay formas adicionales de mejorar el rendimiento del proyecto que no están relacionadas con el marco. Estas mejoras pueden llevar la calificación del proyecto Lighthouse a 100.

  • En la lista de correo de marzo de Algolia , se recomienda agregar una pista preconnectpara aumentar aún más la velocidad de ejecución de consultas. (Es cierto que, desafortunadamente, el fragmento de código incorrecto aparece en el boletín. Aquí está el código correcto).
  • . JS- CSS-, webpack- Gatsby. Gatsby . , , Netlify Amazon S3. , Firebase Hosting, , .
  • Utilizamos imágenes JPEG y PNG cargadas por creadores de inicio en la aplicación. No los comprimimos ni los optimizamos. Mejorar este aspecto de nuestra aplicación es todo un desafío y está más allá del alcance de este proyecto. Además, sería genial si todas estas imágenes se convirtieran al formato WebP. Como resultado, tendríamos que almacenar imágenes usando solo un formato gráfico altamente eficiente. Desafortunadamente, como con muchas otras características de PWA, el equipo de desarrollo de Safari WebKit es adictivo con el soporte de WebP. Ahora es el único navegador importante que no admite este formato.

Resumen


Si resumimos brevemente de qué estábamos hablando aquí, entonces podemos decir lo siguiente:

  • El resultado de la versión "esquelética" de la página durante la carga de datos por parte del cliente crea al usuario una sensación de funcionamiento más rápido del sitio web que cuando el usuario mira una página en blanco mientras el servidor está cargando datos.
  • La versión gatsby del sitio era solo un poco más rápida que la versión Next.js. Sin embargo, el sistema de complemento Gatsby y la documentación del proyecto de alta calidad aumentan la usabilidad de este marco para el desarrollador.

¡Queridos lectores! ¿Utiliza generadores de sitios estáticos o sistemas de representación del lado del servidor para acelerar sus proyectos?


All Articles