Speedy js: 3. critical rendering path (1)

Introducción a las estrategias de optimización del critical rendering path

Fernand Léger

archivado en: JavaScript / 30 octubre, 2015 / taller:

Como vimos en la entrada anterior de esta serie dedicada a la optimización web, en el proceso que lleva desde que intentamos acceder a un sitio web hasta que se muestra, se pasa por dos grandes fases:

  1. Una de comunicación con el exterior, en la que el navegador trata de recopilar toda la información.
  2. Y otra de renderización una vez que el navegador ya dispone de todo el material.

Esta ilustración del libro HTTP, The Definitive Guide, de David Gourley y Brian Totty (O´Reilly, 2002), refleja bien los pasos de la primera fase:

¡En un segundo!

En el material didáctico sobre esta materia, recopilado en un curso muy recomendable que se puede consultar gratuitamente en udacity, los expertos de Google explican que el tiempo que se tarda en empezar a ver la parte superior de un documento web, lo que aparece sin necesidad de scrollear nada, no debería pasar de un segundo. Llaman a este proceso critical rendering path (crp), que traducido vendría a ser algo así como «el camino de la renderización importante».

 

critical-render-path

Un solo segundo podría parecer una meta inalcanzable y más aún si pensamos en los dispositivos móviles, pues como explican estos expertos, las latencias de la red, esto es, el tiempo que tarda en resolverse un viaje entre un cliente y un servidor, son muy altas:

  • Entre 200 y 300 milisegundos para redes 3G en el trayecto de ida y vuelta.
  • Entre 50 y 100 para redes 4G.

Esto significa que solo en la fase de recopilación se van no menos de 600 milisegundos entre que se resuelve la búsqueda DNS, se establece la conexión TCP y se envía y recibe el primer request y response, es decir, el código del primer documento html solicitado; por lo que solo nos quedarían unos 400 milisegundos para resolver toda la fase de renderización si queremos ajustarnos a los criterios que Google considera recomendables.

Es una meta difícil y quizás exagerada -a mí, por ejemplo, me da igual esperar bastantes segundos más cuando consulto la prensa online-, pero sí me parece muy recomendable tratar de alcanzarla para hacer las cosas lo mejor posible. Y este será precisamente el tema que trataré en las próximas entradas: estrategias de optimización para resolver en un segundo el critical rendering path.

Al lector interesado en profundizar en los aspectos más técnicos de esta materia, le recomiendo el libro on-line High Performance Browser Networking, de Ilya Grigorik.

Fase de recopilación

En cierta manera, esta fase es la más complicada, pues apenas se dispone de margen de maniobra. Pero aún así podemos hacer algo... bueno, en realidad, lo que conviene hacer es lo menos posible: enviar el menor número de cosas desde el menor número de sitios. Por orden:

1. Redirecciones: hay que evitar las redirecciones iniciales, pues cada una supone un tiempo precioso en resolverse.

2. DNS y conexiones: conviene que todo el material del critical rendering path, del crp, se encuentre alojado en el mismo servidor, ya que para cada uno hay que pasar por todos los pasos (localización de DNS, conexión TCP). Esto no quiere decir que no podamos enlazar ningún recurso externo, sino que conviene que estos casos no sean fundamentales para la visualización correcta de la parte superior de la página.

Así, por ejemplo, si vamos a incluir un widget de add-this, como el que hay en esta entrada, que tira todo de elementos externos al propio dominio, es mejor disponerlo en la parte inferior, la que no se ve al principio.

Cada recurso que no está en el propio dominio, debe resolverse siguiendo todos los pasos, como ocurre con el componente de add-this de esta página.

Cada recurso que no está en el propio dominio, debe resolverse siguiendo todos los pasos, como ocurre con el componente de add-this de esta página.

 

Por cierto, ni se os ocurra tomar como ejemplo de optimización este blog, que la parte interna está hecha a toda pastilla en un par de domingos resacosos (sí, lo sé, tengo que apañarlo bien algún día de estos...).

3. Reducir la carga inicial: Para que se entienda lo que sigue sin entrar en honduras sobre el protocolo TCP que se establece entre el cliente y el servidor para el envío y la recepción de datos, podemos establecer una analogía con el reparto de pizzas a domicilio. En la maleta de una vespino solo caben 10 pizzas, por lo que si el pedido supera ese número, el pobre repartidor debe realizar dos viajes para entregar la totalidad de las pizzas solicitadas, lo que supone repetir el proceso de llegar a un sitio, aparcar la moto, esperar a que le abran y entregar la mercancía.

Lo mismo sucede con el número de paquetes que se pueden enviar en cada tanda por TCP: el servidor solo puede mandar n cosas de una tacada, luego debe esperar un acuse de recibo -que le abran la puerta- y volver a enviar otra tacada; por lo que es conveniente reducir el número de bytes necesarios para el crp al mínimo posible (creo que el ideal ronda unos 14kb, pero tengo que confirmar este número).

4. Reducir el número de elementos que se envían: al igual que nuestro sufrido pizzero tarda menos si trae dos pizzas de una vez que primero una y luego otra, el navegador tardará menos en preparar una página si tiene que hacer menos viajes. Para que se entienda esto bien, podemos hacer un ejercicio muy sencillo.

Preparamos dos hojas de estilo más sencillas que un botijo, cada una con una sola regla:

Una la podemos llamar foo.css (^^ sep, lo sé, I am Original).

foo.css

.estilo01 {

color: #900;

}

Y otra bar.css (+ de lo mismo).

bar.css

.estilo02 {

color: #009;

}

Y ahora preparamos otra más con las dos reglas juntas.

foobar.css

.estilo01 {

color: #900;

}

.estilo02 {

color: #009;

}

Primero enlazamos las dos páginas por separado.

<link rel="stylesheet" type="text/css" href="foo.css">

<link rel="stylesheet" type="text/css" href="bar.css">

Y analizamos el resultado

En mi servidor, con la castaña de conexión que tengo, una página html con solo dos hojas de estilo prácticamente vacías tarda 696 milisegundos en terminar el DOMContentLoaded, de los que unos 330 se corresponden solo a la descarga, a la recuperación de las hojas.

Sin embargo, en el segundo caso, con las dos reglas juntas en una misma hoja, el tiempo baja hasta los 488 milisegundos, una barbaridad para el reto que nos hemos propuesto: que el usuario ya esté viendo algo interesante en la pantalla en un segundo a más tardar.

css_foobar

¿Por qué? pues porque en el segundo caso nos hemos bajado todo lo que supone el diálogo entre el cliente y el servidor a la mitad (dicho así en plan bestia). Moraleja de esta historia: en producción hay que arrejuntar todas las css y (casi) todo el js en sendos archivos, ya sea con el grunt, su digievolucionado hermano gulp, con una herramienta on-line o a mano o como diantres sea, pero todas las css y (casi todo) el js junto.

Un último ejemplo para que se termine de entender la importancia de reducir el número de archivos enviados: en el momento de escribir estas líneas, el periódico on-line El País envía por lo menos cinco hojas distintas en su portada. A 200 milisegundos de media por envío, solo para recopilarlas todas se pierden hasta 1000 milisegundos, un segundako de los gordos.

css_elpais

Si tenemos en cuenta que, además, solicita de primeras otros tropecientos mil recursos entre js, imágenes y mandangas varias, no es de extrañar que tarde tanto en cargar: 472 solicitudes; 3,5 megas transferidos; 5,81 segundos para el DOMContentLoaded y 7.06 para el Load.

5. Reducir el número de bytes.

Dado que cada byte cuenta en la tarea que tenemos por delante, en producción, cuando ya está todo listo, podemos reemplazar los archivos de trabajo por archivos minificados, esto es, archivos en los que se eliminan datos innecesarios para que las máquinas los lean de forma correcta, como son los comentarios, los espacios en blanco o los nombres de las variables, que minificadas se convierten en meras letras individuales.

Por ejemplo, así empieza la versión 2.1.4 de jQuery sin minificar...

(function( global, factory ) {

if ( typeof module === "object" && typeof module.exports === "object" ) {

...

Y así minificado, sin espacios y todo en una sola línea.

!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=

La diferencia de peso entre el archivo minificado y el normal habla por sí sola:

  • jquery-2.1.4.js: 241 KB
  • jquery-2.1.4.min.js: 82,3 KB

No es un caso excepcional... Por poner otro ejemplo de una librería recurrente:

  • bootstrap.js: 14kb
  • bootstrap.min.js: 8kb

En síntesis: todas las css y todos los js, minificados, una tarea muy fácil de hacer hoy en día gracias a herramientas como grunt o gulp.

Minificar el html puede provocar problemas innecesarios. Mejor ni intentarlo ; )

Bueno, como vemos, podemos hacer muchas cosas para mejorar la obtención del material a renderizar y aún nos faltan un montón más por ver, como la comprensión gzip, el tratamiento de fuentes, de imágenes y de la caché. Pero eso será en la próxima entrada dedicada a este tema, que por hoy ya está bien : )

 

|| Tags: ,

valoración de los lectores sobre Speedy js: 3. critical rendering path (1)

  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • 5 sobre 5 (3 votos)

¿Te ha parecido útil o interesante esta entrada?
dormido, valoración 1 nadapensativo, valoración 2 un poco sonrisa, valoración 3 a medias guiño, valoración 4 bastante aplauso, valoración 5 mucho

Tú opinión es muy importante, gracias por compartirla!

Aportar un comentario


*