Cambiar a Next.js y acelerar la carga de la p谩gina de inicio de manifold.co 7.5 veces

Hoy estamos publicando una traducci贸n de una historia sobre c贸mo la transici贸n de React Boilerplate a Next.js , un marco para desarrollar aplicaciones web progresivas basadas en React, ha acelerado la carga de la p谩gina de inicio del proyecto manifold.co en 7.5 veces. No se hicieron otros cambios al proyecto, y esta transici贸n, en general, result贸 ser completamente invisible para otras partes del sistema. Lo que result贸 al final result贸 ser incluso mejor de lo esperado.



Resumen de resultados


De hecho, podemos decir que la transici贸n a Next.js nos dio algo as铆 como "un aumento de la productividad del proyecto que surgi贸 de la nada". As铆 es como se ve el tiempo de carga del proyecto cuando se utilizan varios recursos de hardware y conexiones de red.
Conexi贸n
CPU
A segundos
Despu茅s de segundos
% De mejora
R谩pido (200 Mbps)
R谩pido
1,5
0.2 0.2
750
Mediano (3G)
R谩pido
5.6
1.1
500
Mediano (3G)
Medio
7.5
1.3
570
Lento (conexi贸n 3G lenta)
Medio
22
4 4
550

Cuando se usa una conexi贸n r谩pida y un dispositivo con un procesador r谩pido, el tiempo de carga del sitio cay贸 de 1.5 s. hasta 0.2 s., es decir, este indicador mejor贸 7.5 veces. En una conexi贸n de calidad media y en un dispositivo con un rendimiento promedio, el tiempo de carga del sitio cay贸 de 7,5 s. hasta 1.3 s

驴Qu茅 sucede despu茅s de que un usuario hace clic en una URL?


Para comprender las caracter铆sticas del trabajo de las aplicaciones web progresivas (Progressive Web App, PWA), primero debe comprender lo que sucede entre el momento en que el usuario hace clic en la URL (en la direcci贸n de nuestro sitio web) y el momento en que ve algo en una ventana del navegador (en este caso, nuestra aplicaci贸n React).


Etapas de aplicaci贸n

Considere las 5 etapas de trabajar con la aplicaci贸n, cuyo diagrama se proporciona arriba.

  1. El usuario accede a la URL, el sistema descubre la direcci贸n del servidor utilizando DNS y accede al servidor. Todo esto se hace extremadamente r谩pido, por lo general toma menos de 100 milisegundos, pero este paso lleva algo de tiempo, por eso se menciona aqu铆.
  2. Ahora el servidor devuelve el c贸digo HTML de la p谩gina, pero la p谩gina en el navegador permanece vac铆a hasta que se cargan los recursos necesarios para su visualizaci贸n (a menos que los recursos se carguen asincr贸nicamente ). En realidad, se est谩n llevando a cabo m谩s acciones en esta etapa de las que se muestran en el diagrama, pero una revisi贸n conjunta de todos estos procesos tambi茅n nos conviene.
  3. Despu茅s de cargar el c贸digo HTML y los recursos m谩s importantes, el navegador comienza a mostrar lo que puede mostrar y contin煤a cargando todo lo dem谩s (im谩genes, por ejemplo) en segundo plano. 驴Te has preguntado alguna vez por qu茅 las im谩genes a veces "aparecen" repentinamente en una p谩gina, obviamente, m谩s r谩pido de lo necesario, y otras veces se cargan demasiado? Esto es precisamente por qu茅 sucede esto. Este enfoque le permite crear r谩pidamente una p谩gina terminada.
  4. El c贸digo JavaScript se puede analizar y ejecutar solo despu茅s de cargarlo. Dependiendo del tama帽o del c贸digo JS utilizado en la p谩gina (y esto puede ser, para una aplicaci贸n React t铆pica, bastante grande si el c贸digo est谩 empaquetado en un solo archivo), esto puede llevar varios segundos o incluso m谩s (tenga en cuenta que JS el c贸digo no necesita, para comenzar a ejecutarse, esperar la carga de todos los dem谩s recursos, a pesar de que en el diagrama se ve exactamente as铆).
  5. En el caso de una aplicaci贸n React, ahora llega el momento en que el c贸digo modifica el DOM, lo que hace que el navegador redibuje la p谩gina ya mostrada. Entonces comienza otro ciclo de carga de recursos. El tiempo que tome este paso depender谩 de la complejidad de la p谩gina.

Cuanto m谩s r谩pido, mejor.


Dado que una aplicaci贸n web progresiva toma el c贸digo React y produce c贸digo est谩tico HTML y CSS, esto significa que el usuario ve la aplicaci贸n React ya en el paso 3 del esquema anterior, y no en el paso 5. En nuestras pruebas, esto toma 0.2-4 segundos , que depende de la velocidad de conexi贸n del usuario a Internet y de su dispositivo. Esto es mucho mejor que los 1.5-22 segundos anteriores. Las aplicaciones web progresivas son una forma confiable de entregar aplicaciones React m谩s r谩pido al usuario.

La raz贸n por la cual las aplicaciones web progresivas y los frameworks relacionados como Next.js a煤n no son muy populares es porque, tradicionalmente, los frameworks JS no son particularmente exitosos en la generaci贸n de c贸digo HTML est谩tico. Hoy, todo ha cambiado mucho debido al hecho de que los frameworks como React, Vue y Angular, y otros, tienen un excelente soporte para la representaci贸n del lado del servidor. Sin embargo, para utilizar estas herramientas, a煤n necesita un conocimiento profundo de las caracter铆sticas del trabajo de los agrupadores y las herramientas para crear proyectos. Trabajar con todo esto no est谩 exento de problemas.

La reciente aparici贸n de marcos de PWA como Next.js y Gatsby (ambos aparecieron a fines de 2016 - principios de 2017) se ha convertido en un paso serio hacia la adopci贸n generalizada de PWA debido a las barreras de entrada m谩s bajas y al hecho de que el uso de dichos marcos es una tarea simple y agradable.

Aunque no todas las aplicaciones se pueden transferir a Next.js, para muchas aplicaciones React esta transici贸n significa el mismo "rendimiento de la nada" del que estamos hablando aqu铆, complementado por un uso a煤n m谩s eficiente de los recursos de la red.

驴Qu茅 tan dif铆cil es migrar a Next.js?


En general, se puede notar que traducir nuestra p谩gina de inicio a Next.js no fue muy dif铆cil. Sin embargo, encontramos algunas dificultades causadas por las caracter铆sticas de arquitectura de nuestra aplicaci贸n.

鈻 Rechazar un enrutador React


Tuvimos que abandonar el enrutador React porque Next.js tiene su propio enrutador incorporado, que se combina mejor con optimizaciones con respecto a la separaci贸n de c贸digo realizada sobre la arquitectura PWA. Esto permite que este enrutador proporcione una carga de p谩gina mucho m谩s r谩pida de lo que esperar铆a de cualquier enrutador del lado del cliente.

El enrutador Next.js es un enrutador React de alta velocidad, pero todav铆a no es un enrutador React.

En la pr谩ctica, dado que no aprovechamos las caracter铆sticas particularmente avanzadas que ofrece el enrutador React, la transici贸n al enrutador Next.js para nosotros fue simplemente reemplazar el componente enrutador React est谩ndar con el componente Next.js correspondiente:

/*   ( React) */ <Link to="/my/page">  A link </Link> /*   ( Next.js) */ <Link href="/my/page" passHref>  <a>    A link  </a> </Link> 

En general, todo result贸 no ser tan malo. Tuvimos que cambiar el nombre de la propiedad y agregar una etiqueta para fines de representaci贸n del servidor. Como tambi茅n utilizamos la biblioteca de styled-components , result贸 que en la mayor铆a de los casos necesit谩bamos agregar la propiedad passHref para garantizar que el sistema se comporta de tal manera que href siempre apunte a la etiqueta generada.


Solicitudes de red para manifold.co

Para ver con sus propios ojos la optimizaci贸n del enrutador Next.js en acci贸n, abra la pesta帽a Red de las herramientas de desarrollo del navegador al ver la p谩gina manifold.co y haga clic en alg煤n enlace. La figura anterior muestra el resultado de hacer clic en el enlace /services . Como puede ver, conduce a la ejecuci贸n de la solicitud para cargar services.js lugar de la solicitud habitual.

No estoy hablando solo del enrutamiento del lado del cliente; el enrutador React tambi茅n es adecuado para resolver este problema. Estoy hablando de una pieza real de c贸digo JavaScript que se extrajo del resto del c贸digo y se carg贸 a pedido. Esto se hace usando el est谩ndar Next.js. Y esto es mucho mejor que lo que ten铆amos antes. Es decir, estamos hablando de un gran paquete de c贸digo JS con un tama帽o de 1.7 MB, que el cliente, antes de que pudiera ver algo, tuvo que descargar y procesar.

Aunque la soluci贸n presentada aqu铆 no es perfecta, est谩 mucho m谩s cerca que la anterior a la idea de que los usuarios solo descargan c贸digo para las p谩ginas que ven.

鈻 Caracter铆sticas del uso de Redux


Continuando con el tema de las dificultades asociadas con la transici贸n a Next.js, se puede observar que todas las optimizaciones interesantes que Next.js experimenta la aplicaci贸n tienen un cierto impacto en esta aplicaci贸n. Es decir, dado que Next.js realiza la separaci贸n del c贸digo a nivel de p谩gina, evita que el desarrollador acceda al componente ra铆z React o al m茅todo render() de la biblioteca react-dom . Si ya ha estado involucrado en la configuraci贸n de Redux, entonces puede notar que todo esto nos dice que para la operaci贸n normal con Redux necesitamos resolver el problema, que es que no est谩 claro exactamente d贸nde buscar Redux.

Next.js proporciona un componente especial de orden superior, con withRedux , que withRedux como un contenedor para todos los componentes de nivel superior en cada p谩gina:

 export default withRedux(HomePage); 

Aunque todo esto no es tan malo, pero si necesita m茅todos createStore() , como cuando usa inyectores reductor-reductor , espere que necesitar谩 tiempo adicional para depurar el envoltorio (y por cierto, nunca intente use algo como redux-reducer-injectors ).

Adem谩s, debido al hecho de que Redux ahora es una "caja negra", el uso de la biblioteca Inmutable se vuelve problem谩tico. Aunque el hecho de que Immutable funcione con Redux parece bastante obvio, me encontr茅 con un problema. Entonces, o el estado de nivel superior no era inmutable (el get is not a function error de get is not a function ), o el componente contenedor intent贸 usar la notaci贸n de puntos para trabajar con objetos JS en lugar del m茅todo .get() ( Can't get catalog of undefined errores Can't get catalog of undefined ). Para depurar este problema, tuve que referirme al c贸digo fuente. Despu茅s de todo, Next.js obliga al desarrollador a usar sus propios mecanismos por una raz贸n.

En general, se puede observar que el principal problema asociado con Next.js es que muy poco en este marco est谩 bien documentado. Hay muchos ejemplos en la documentaci贸n sobre la base de los cuales puede crear algo propio, pero si entre ellos no hay uno que refleje las caracter铆sticas de su proyecto, solo puede desear buena suerte.

鈻岶etch rechazo


Utilizamos la biblioteca react-inlinesvg , que ofrece opciones de estilo para im谩genes SVG incrustadas y almacenamiento en cach茅 de consultas. Pero aqu铆 tuvimos un problema: al realizar la representaci贸n del servidor, no existen las solicitudes XHR (al menos no en el sentido de las URL generadas por Webpack, como es de esperar). Los intentos de ejecutar tales solicitudes interfieren con la representaci贸n del servidor.

Aunque hay otras bibliotecas para trabajar con datos SVG incrustados que admiten SSR, decid铆 abandonar esta funci贸n, ya que los archivos SVG todav铆a rara vez se usaban. Los reemplac茅 con im谩genes normales, etiquetas <img> , si no necesitaba estilo al mostrar las im谩genes correspondientes, o las insert茅 en el c贸digo en forma de React JSX. Probablemente, todo mejor贸, ya que las ilustraciones de JSX ahora llegaron al navegador cuando se carg贸 la p谩gina por primera vez y el paquete JS enviado al cliente ten铆a 1 biblioteca menos.

Si necesita usar mecanismos de carga de datos (necesitaba esta caracter铆stica para otra biblioteca), puede configurarlo con next.config.js , usando whatwg-fetch y node-fetch :

 module.exports = { webpack: (config, options) =>   Object.assign(config, {     plugins: config.plugins.concat([       new webpack.ProvidePlugin(         config.isServer           ? {}           : { fetch: 'imports-loader?this=>global!exports-loader?global.fetch!whatwg-fetch' }       ),     ]),   resolve: Object.assign(config.resolve, {     alias: Object.assign(       config.resolve.alias,       config.isServer ? {} : { fetch: 'node-fetch' }     ),   }), }), }; 

鈻 Cliente y servidor JS


La 煤ltima caracter铆stica de Next.js, que me gustar铆a mencionar aqu铆, es que este marco se inicia dos veces, una para el servidor y otra para el cliente. Esto difumina ligeramente la l铆nea entre el c贸digo JavaScript del lado del cliente y el c贸digo Node.js en la misma base de c贸digo, lo que provoca errores inusuales como fs is undefined cuando se intenta aprovechar las caracter铆sticas de Node.js en el cliente.

Como resultado, tenemos que construir tales construcciones en next.js.config :

 module.exports = { webpack: (config, options) =>   Object.assign(config, {     node: config.isServer ? undefined : { fs: 'empty' },   }), }; 

El indicador config.isServer en Webpack ser谩 su mejor amigo si necesita ejecutar el mismo c贸digo en diferentes entornos.

Adem谩s, Next.js admite, adem谩s de los m茅todos est谩ndar para el ciclo de vida de los componentes React, el m茅todo getInitialProps() , que se llama solo cuando el c贸digo se getInitialProps() en modo servidor:

 class HomePage extends React.Component { static getInitialProps() {   //         } componentDidMount() {   //     ,    } 鈥 } 

S铆, y no olvidemos que nuestro buen amigo, el objeto de window , necesario para organizar la escucha de eventos, para determinar el tama帽o de la ventana del navegador y dar acceso a muchas funciones 煤tiles, no est谩 disponible en Node.js:

 if (typeof window !== 'undefined') { // ,     `window`      } 

Cabe se帽alar que incluso Next.js no puede salvar al desarrollador de la necesidad de resolver problemas asociados con la ejecuci贸n del mismo c贸digo en el servidor y en el cliente. Pero al resolver tales problemas, config.isServer y getInitialProps() son muy 煤tiles.

Resultados: 驴qu茅 pasar谩 despu茅s de Next.js?


A corto plazo, el marco Next.js coincide perfectamente, en t茅rminos de rendimiento, con nuestros requisitos para la representaci贸n del servidor y la capacidad de ver nuestro sitio en dispositivos que tienen JavaScript deshabilitado. Adem谩s, ahora le permite usar metaetiquetas avanzadas (ricas).

Quiz谩s en el futuro consideremos otras opciones en caso de que nuestra aplicaci贸n necesite tanto la representaci贸n del servidor como una l贸gica de servidor m谩s compleja (por ejemplo, consideramos la posibilidad de implementar tecnolog铆a de inicio de sesi贸n 煤nico en manifold.co y dashboard.manifold.co ) Pero hasta entonces usaremos Next.js, ya que este marco, con peque帽os costos de tiempo, nos trajo enormes beneficios.

Estimados lectores! 驴Usas Next.js en tus proyectos?

Source: https://habr.com/ru/post/442654/


All Articles