Gestión avanzada de assets en WordPress

Pablo López

Puedes encontrarme...

  • Twitter

    En Twitter: @desarrollowp

    Twitteo artículos, información y noticias sobre WordPress y el sector web en general.
  • Blog

    En mi blog: https://desarrollowp.com

    Blog sobre tutoriales, guías, consejos, noticias y entrevistas en el mundo WordPress.
  • Slack

    En Slack

    En los slacks de wpes y wpmadrid.
  • WordPress

    En la comunidad

    Ayudo a organizar el grupo de meetup de Madrid WordPress y la WordCamp Madrid.
  • Trabajo

    WordPress developer en UNIR

    Trabajo con WordPress tratando de hacer cada proyecto mejor que el anterior.
Disclaimer

Disclaimer

 

Soy responsable de lo que digo, no de lo que vosotros interpretéis

La velocidad es importante

Menos es más

 

La petición HTTP más rápida es aquella que no se hace. Piensa si "eso" es realmente necesario.

Verdad verdadera

Regla del 80 / 20

 

Conseguir el 80% de la optimización consume el 20% del esfuerzo, y el 20% restante de optimización nos supondrá el 80% del esfuerzo

Vilfredo Pareto

90 / 10

Gestión de assets en WordPress

 

Optimizar el desarrollo y la gestión de tus recursos estáticos (Imágenes, CSS y JS) influirá en la velocidad de carga y rendimiento de tu página web.

Mr. Google

Herramientas de medición

Las 9 reglas de Google

  • Evitar redirecciones a páginas de destino

  • Habilitar la compresión

  • Mejorar el tiempo de respuesta del servidor

  • Aprovechar el almacenamiento en caché de los navegadores

  • Minificar recursos

  • Optimizar imágenes

  • Optimizar la entrega de CSS

  • Priorizar el contenido visible

  • Quitar el JavaScript que bloquea el renderizado del contenido

Enlace a la página de desarrolladores de Google

Habilitar la compresión


Esta regla se activa cuando PageSpeed Insights detecta que se han publicado recursos comprimibles sin compresión gzip.

En todos los navegadores modernos se admite y negocia automáticamente la compresión gzip en todas las solicitudes HTTP.

Al habilitar la compresión gzip, se puede reducir el tamaño de las respuestas transferidas hasta en un 90%, lo que puede, a su vez, disminuir significativamente el tiempo necesario para descargar el recurso correspondiente, así como reducir el uso de datos del cliente y el tiempo que se tarda en renderizar las páginas por primera vez.

  • Apache: usa mod_deflate (algoritmo de compresión de datos sin pérdidas).
  • Nginx: usa ngx_http_gzip_module.
  • IIS: configura la compresión HTTP.

Habilitar la compresión

Configuración en apache (.htaccess)

Añade en el fichero .htaccess las siguientes reglas.

Más información en GTmetrics

<IfModule mod_deflate.c>
  # Compress HTML, CSS, JavaScript, Text, XML and fonts
  AddOutputFilterByType DEFLATE application/javascript
  AddOutputFilterByType DEFLATE application/rss+xml
  AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
  AddOutputFilterByType DEFLATE application/x-font
  AddOutputFilterByType DEFLATE application/x-font-opentype
  AddOutputFilterByType DEFLATE application/x-font-otf
  AddOutputFilterByType DEFLATE application/x-font-truetype
  AddOutputFilterByType DEFLATE application/x-font-ttf
  AddOutputFilterByType DEFLATE application/x-javascript
  AddOutputFilterByType DEFLATE application/xhtml+xml
  AddOutputFilterByType DEFLATE application/xml
  AddOutputFilterByType DEFLATE font/opentype
  AddOutputFilterByType DEFLATE font/otf
  AddOutputFilterByType DEFLATE font/ttf
  AddOutputFilterByType DEFLATE image/svg+xml
  AddOutputFilterByType DEFLATE image/x-icon
  AddOutputFilterByType DEFLATE text/css
  AddOutputFilterByType DEFLATE text/html
  AddOutputFilterByType DEFLATE text/javascript
  AddOutputFilterByType DEFLATE text/plain
  AddOutputFilterByType DEFLATE text/xml
</IfModule>

Habilitar la compresión

Ejemplos

A continuación un listado de recursos y su peso antes y después de comprimir con gzip.

Más información en la página de desarrolladores de Google

Biblioteca Tamaño Tamaño comprimido Compresión
jquery-1.11.0.js 276 KB 82 KB 70%
jquery-1.11.0.min.js 94 KB 33 KB 65%
angular-1.2.15.js 729 KB 182 KB 75%
angular-1.2.15.min.js 101 KB 37 KB 63%
bootstrap-3.1.1.css 118 KB 18 KB 85%
bootstrap-3.1.1.min.css 98 KB 17 KB 83%
foundation-5.css 186 KB 22 KB 88%
foundation-5.min.css 146 KB 18 KB 88%

Aprovechar el almacenamiento en caché de los navegadores


Esta regla se activa cuando PageSpeed Insights detecta que en la respuesta de tu servidor no se incluyen encabezados de caché explícitos, o que se ha indicado que los recursos se almacenen en la memoria caché durante poco tiempo.

Obtener recursos a través de la red es un proceso lento y costoso (ciclo de ida y vuelta entre un cliente y un servidor para descargarse.

Esto retrasa el procesamiento y puede bloquear el renderizado del contenido de las páginas. Además, este proceso puede suponer costes de datos a los visitantes.

Con Cache-Control se define cómo y durante cuánto tiempo los navegadores pueden almacenar una respuesta concreta.

Con ETag se proporciona un token de revalidación que los navegadores envían automáticamente para verificar si el recurso en cuestión ha cambiado desde la última vez que se solicitó. El servidor web no necesita enviar una respuesta completa si el contenido no ha cambiado.

Aprovechar el almacenamiento en caché de los navegadores

Leverage browser caching

Almacena recursos en la caché del navegador evitando tener que ir a buscarlos al servidor. Evitamos transferencia y pérdida de tiempo consultando en cada petición al servidor.

Browser Cache

Aprovechar el almacenamiento en caché de los navegadores

Leverage browser caching

Añadimos en el .htaccess las siguientes reglas.

Más información en GTmetrics

<IfModule mod_expires.c>
  # Expires Caching
  ExpiresActive On
  ExpiresByType image/jpg "access plus 1 year"
  ExpiresByType image/jpeg "access plus 1 year"
  ExpiresByType image/gif "access plus 1 year"
  ExpiresByType image/png "access plus 1 year"
  ExpiresByType text/css "access plus 1 month"
  ExpiresByType application/pdf "access plus 1 month"
  ExpiresByType text/x-javascript "access plus 1 month"
  ExpiresByType application/x-shockwave-flash "access plus 1 month"
  ExpiresByType image/x-icon "access plus 1 year"
  ExpiresDefault "access plus 2 days"
</IfModule>

Aprovechar el almacenamiento en caché de los navegadores

Leverage browser caching

.htaccess completo en GitHub

Browser Cache

Minificar recursos


Esta regla se activa cuando PageSpeed Insights detecta que podría reducirse el tamaño de uno de tus recursos mediante la minificación.

Minificar es el proceso mediante el cual se eliminan datos innecesarios (comentarios, saltos de línea, emplear nombres de variables más cortos...) o redundantes de un recurso sin que se vea afectada la forma en que los navegadores lo procesan.

¿Qué se puede minificar?

  • HTML
  • CSS
  • JavaScript

Minificar recursos

Ejemplos

A continuación un listado de recursos y su peso antes y después de minificarlos.

Biblioteca Tamaño Dev Tamaño Min Ahorro
Font Awesome 5.0.12 46 KB 37 KB 20%
Bootstrap 4.1 CSS 169 KB 138 KB 18%
Bootstrap 4.1 JS 120 KB 50 KB 58%
jQuery 3.3.1 266 KB 85 KB 68%
Foundation 6.4.2 CSS 153 KB 115 KB 25%
Foundation 6.4.2 JS 413 KB 146 KB 65%

Minificar recursos

Minificando el HTML

En el caso del HTML, además de minificar podemos eliminar:

  • HTML de la cabecera de WordPress
  • Clases que no utilizamos en body, articles, menús... (body_class, post_class, nav_menu_css_class)
  • QueryString versión WordPress
// Pasar de esto...
<!DOCTYPE html>
<html lang="es-ES" prefix="og: http://ogp.me/ns#">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>El blog de desarrollo WordPress en español con tutoriales</title>
<link rel="preload" href="https://desarrollowp.com/wp-content/themes/desarrollowp/css/styles.min.css" as="style">
<link rel="preload" href="https://desarrollowp.com/wp-includes/js/jquery/jquery.js" as="script">
<link rel="preload" href="https://desarrollowp.com/wp-content/themes/desarrollowp/js/scripts.min.js" as="script">
<meta name="description" content="Aquí podrás encontrar trucos..."/>
<link rel="canonical" href="https://desarrollowp.com/" />
<link rel="publisher" href="https://plus.google.com/+PabloLopezMestre"/>


// a esto otro...
<!DOCTYPE html><html lang="es-ES" prefix="og: http://ogp.me/ns#"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>El blog de desarrollo WordPress en español con tutoriales</title><link rel="preload" href="https://desarrollowp.com/wp-content/themes/desarrollowp/css/styles.min.css" as="style"><link rel="preload" href="https://desarrollowp.com/wp-includes/js/jquery/jquery.js" as="script"><link rel="preload" href="https://desarrollowp.com/wp-content/themes/desarrollowp/js/scripts.min.js" as="script"><meta name="description" content="Aquí podrás encontrar trucos..."/><link rel="canonical" href="https://desarrollowp.com/" /><link rel="publisher" href="https://plus.google.com/+PabloLopezMestre"/>

Minificar recursos

Minificando el HTML

Reduce y elimina HTML inncesario de la cabecera de WordPress.

Más información aquí

// Elimina HTML del head
function remove_headlinks_and_emojis() {
  remove_action( 'wp_head', 'wp_generator' );
  remove_action( 'wp_head', 'rsd_link' );
  remove_action( 'wp_head', 'wlwmanifest_link' );
  remove_action( 'wp_head', 'start_post_rel_link' );
  remove_action( 'wp_head', 'index_rel_link' );
  remove_action( 'wp_head', 'wp_shortlink_wp_head' );
  remove_action( 'wp_head', 'adjacent_posts_rel_link' );
  remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0 );
  remove_action( 'wp_head', 'parent_post_rel_link' );
  remove_action( 'wp_head', 'feed_links', 2 );
  remove_action( 'wp_head', 'feed_links_extra', 3 );
  remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
  remove_action( 'wp_print_styles', 'print_emoji_styles' );
  remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' );
  remove_filter( 'the_content_feed', 'wp_staticize_emoji' );
  remove_filter( 'comment_text_rss', 'wp_staticize_emoji' );
}
add_action( 'init', 'remove_headlinks_and_emojis' );

Minificar recursos

Minificando el HTML

Elimina clases que no utilizas en body, articles, menús... (body_class, post_class, nav_menu_css_class).

Ejemplo, por defecto en la etiqueta body del hola mundo de un twentyseventeen aparecen las siguientes clases: post-template-default single single-post postid-1 single-format-standard has-header-image has-sidebar colors-light.

Más información aquí

// Eliminar las clases del body
function remove_body_classes($classes) {
  $whitelist = array( 'home', 'error404' );
  $wp_classes = array_intersect( $classes, $whitelist );
 
  return $wp_classes;
}
add_filter( 'body_class', 'remove_body_classes');
 
// Eliminar las clases del article
function remove_post_classes($classes) {
    $whitelist = array( 'post' );
    $wp_classes = array_intersect( $classes, $whitelist );
 
    return $wp_classes;
}
add_filter( 'post_class', 'remove_post_classes');
 
// Eliminar las clases del menú
function remove_menu_classes($classes) {
    $whitelist = array( 'menu-item' );
    $wp_classes = array_intersect( $classes, $whitelist );
 
    return $wp_classes;
}
add_filter( 'nav_menu_css_class', 'remove_menu_classes');

Minificar recursos

Minificando el HTML

Elimina el parámetro versión en los assets de WordPress. Ejemplo: ?ver=4.9.6

Más información aquí

// Eliminar las versiones de los parámetros en las URLs
function remove_version_params( $src ){
  $parts = explode( '?ver', $src );
 
  return $parts[0];
}
add_filter( 'script_loader_src', 'remove_version_params', 15, 1 );
add_filter( 'style_loader_src', 'remove_version_params', 15, 1 );

Minificar recursos

Minificando el HTML

En GitHub podemos encontrar varias clases para lograr minificar el HTML. A través de expresiones regulares se eliminan los espacios, saltos de línea, tabulaciones, comentarios de desarrollo, etc...

Clase PHP para minificar HTML

HTML Minifier

HTML Minifier

Repositorio Plugins

Minificar recursos

Minificando el CSS

Podemos hacer uso de herramientas como Gulp.

Gulp te ayuda a automatizar tareas comunes en el desarrollo de una aplicación, como pueden ser: mover archivos de una carpeta a otra, eliminarlos, minificar código, concatenación, validar sintáxis...

//Configuración de la tarea CSS
gulp.task('css', function () {
  gulp.src(['css/bootstrap.min.css','css/font-awesome.min.css','css/styles.css'])
    .pipe(concat('styles-concat.css'))
    .pipe(minifyCss())
    .pipe(rename('styles.min.css'))
    .pipe(gulp.dest('css')
  );
});

Minificar recursos

Minificando el JS

También con Gulp.

En JS entra en juego Uglify, que permite una mayor ahorro de KB respecto a lo que podemos hacer en los recursos CSS.

//Configuración de la tarea JS
gulp.task('js', function () {
  gulp.src(['js/bootstrap.min.js','js/lazyload.min.js','js/main.js'])
    .pipe(concat('scripts-concat.js'))
    .pipe(uglify())
    .pipe(rename('scripts.min.js'))
    .pipe(gulp.dest('js')
  );   
});

Optimizar imágenes


Esta regla se activa cuando PageSpeed Insights detecta que las imágenes de la página analizada pueden optimizarse para reducir su tamaño de archivo sin afectar significativamente la calidad visual.

Las imágenes representan gran parte del peso de una página, optimizarlas es clave.

  • El tamaño
  • La calidad/resolución
  • El formato
  • Imágenes responsive
  • Lazy Load

Optimizar imágenes

Optimizar imágenes en tamaño

Cuanto más grande sea una imagen, más pesa. Subiremos imágenes al tamaño definido en nuestro diseño para evitar el error de Serve scaled images.

Optimize Images
Tamaño Peso
5757 x 3238 px 4,42 MB
1024 x 576 px 552 KB

Optimizar imágenes

Optimizar imágenes en calidad/resolución

Lo ideal es tratar las imágenes en software de edición como Photoshop o Gimp.

Optimize Images
Guardar como Peso
Calidad máxima (12) 552 KB
Para web (60) 147 KB

Optimizar imágenes

Optimizar imágenes en calidad/resolución

Existen muchos plugins que nos pueden ayudar a optimizar las imágenes que subamos, e incluso optimizar en lote las existentes.

Smush Image Compression and Optimization, Imagify, Kraken, EWWW

Optimizar imágenes

Formato de imágenes

Las imágenes en formato GIF, PNG o JPEG representan el 96% del total de imágenes de Internet.

Los formatos GIF y PNG son formatos de compresión sin pérdida, ya que no se produce ningún cambio visual en las imágenes al comprimirlas.

El formato JPEG es un formato con pérdida; es decir, el proceso de compresión elimina detalles visuales de las imágenes. No obstante, la tasa de compresión puede ser 10 veces mayor que la de los formatos GIF y PNG.

Más información en la página de desarrolladores de Google

GIF, PNG, JPEG

Optimizar imágenes

Haz uso de las imágenes responsive

Desde la versión 4.4 WordPress incorpora de manera nativa los atributos srcset y sizes en las etiquetas img que genera.

Al subir una imagen WordPress genera una serie de miniaturas (por defecto a 150px, 300px, 768px y 1024px). Se incluyen las miniaturas disponibles el el atributo srcset, y los navegadores muestran la adecuada a cada dispositivo.

Para ayudar a los navegadores a elegir la mejor imagen de la lista de srcset, se incluyen los tamaños en el atributo sizes.

Más información aquí.

<img
width="848"
height="477"
src="imagen.jpg"
srcset="imagen.jpg 848w, imagen-300x169.jpg 300w, imagen-768x432.jpg 768w"
sizes="(max-width: 848px) 100vw, 848px"
alt="imagen responsive"
class="img-responsive wp-post-image">

Optimizar imágenes

Añade nuevos "puntos de corte"

WordPress nos permite añadir nuevos thumbnails además de los que vienen por defecto.

Más información aquí. Post de Mau.

add_image_size( string $name, int $width, int $height, bool|array $crop = false )


function my_custom_image_size() {
  add_image_size( 'my-custom-size', 512 );
}
add_action( 'after_setup_theme', 'my_custom_image_size' );

Optimizar imágenes

Funciones y hooks disponibles

Tenemos a nuestra disposición varias funciones y hooks útiles si estamos desarrollando un tema y/o plugin a medida.

Más información aquí

wp_get_attachment_image_srcset(int $attachment_id, array|string $size, array $image_meta) – Devuelve el valor del atributo srcset de una imagen.

wp_calculate_image_srcset(array $size_array, string $image_src, array $image_meta,  int $attachment_id) – Función auxiliar para calcular los tamaños a incluir en el atributo srcset.

wp_get_attachment_image_sizes(int $attachment_id, array|string $size, array $image_meta) – Devuelve el valor del atributo sizes de una imagen.

wp_calculate_image_sizes(array|string $size, string $image_src, array $image_meta, int $attachment_id) – Función auxiliar para calcular los tamaños a incluir en el atributo sizes.

wp_make_content_images_responsive(string $content) – Convierte las imágenes de un texto en responsive (siempre que tengan la clase wp-image-XX).

wp_image_add_srcset_and_sizes(string $image, array $image_meta, int $attachment_id) – Añade los atributos srcset y sizes a una imagen. Se usa en la función anterior.

Optimizar imágenes

Lazy Load

Lazy load, carga diferida o carga perezosa es una técnica que consiste en retrasar la carga de ciertos tipos de contenido como imágenes, vídeos y frames.

Esto se consigue reemplazando el src y srcset de una imagen por un pixel en blanco, de este modo ahorramos la petición http y el hecho de descargar una imagen que esté fuera del área visible.

Cuando hagamos scroll, mediante javascript se iran reemplazando estos atributos por los reales, y se irán mostrando las imágenes sólo si llegamos a ellas.

Más información aquí

Lazy Load

Optimizar imágenes

Image Optimization Book

Resumiendo: Busca un equilibrio entre la calidad y el peso, elige un formato adecuado, súbelas al tamaño indicado, utiliza imágenes responsive y aplaza la carga mediante Lazy Load.

Más información en https://images.guide

Image Optimization Book

¿Hasta aquí todo claro?

 

Optimizar la entrega de CSS


Esta regla se activa cuando PageSpeed Insights detecta que en una página se incluyen hojas de estilo externas que bloquean el renderizado.

Para renderizar el contenido de las páginas, los navegadores deben descargar y procesar todas las hojas de estilo externas.

Más información en la página de desarrolladores de Google

Optimizar la entrega de CSS

¿Qué nos recomienda Google?

El consejo es cargar en línea el Critical CSS y de forma asíncrona el resto.

<html>
  <head>
    <style>
      .blue{color:blue;}
    </style>
  </head>
  <body>
    <div class="blue">
      Hello, world!
    </div>
    <noscript id="deferred-styles">
      <link rel="stylesheet" type="text/css" href="small.css"/>
    </noscript>
    <script>
      var loadDeferredStyles = function() {
        var addStylesNode = document.getElementById("deferred-styles");
        var replacement = document.createElement("div");
        replacement.innerHTML = addStylesNode.textContent;
        document.body.appendChild(replacement)
        addStylesNode.parentElement.removeChild(addStylesNode);
      };
      var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
      if (raf) raf(function() { window.setTimeout(loadDeferredStyles, 0); });
      else window.addEventListener('load', loadDeferredStyles);
    </script>
  </body>
</html>

Optimizar la entrega de CSS

Critical CSS

Generaremos un archivo critical.min.css donde incluir todos los estilos que afectan al primer renderizado de la web.

Este archivo lo cargaremos en línea.

function print_critical_css() {?>
  <style>
    <?php include_once get_theme_file_path('css/critical.min.css');?>
  </style>
<?php }

add_action( 'wp_print_styles', 'print_critical_css', 1);

Optimizar la entrega de CSS

Uso del atributo media

Generaremos una hoja de estilos para cada media-query, de este modo no bloquearemos el renderizado. Por ejemplo, en bootstrap disponemos de las siguientes media queries:

// Small devices (landscape phones, 576px and up)
@media (min-width: 576px) { ... }

// Medium devices (tablets, 768px and up)
@media (min-width: 768px) { ... }

// Large devices (desktops, 992px and up)
@media (min-width: 992px) { ... }

// Extra large devices (large desktops, 1200px and up)
@media (min-width: 1200px) { ... }

wp_enqueue_style( string $handle, string $src = '', array $deps = array(), string|bool|null $ver = false, string $media = 'all' )

function enqueue_styles_by_media() {
  wp_enqueue_style( 'styles-small', get_theme_file_uri('/css/styles-small.min.css'), '', null, '(min-width:576px)' );
  
  wp_enqueue_style( 'styles-medium', get_theme_file_uri('/css/styles-medium.min.css'), '', null, '(min-width:768px)' );

  wp_enqueue_style( 'styles-large', get_theme_file_uri('/css/styles-large.min.css'), '', null, '(min-width:992px)' );

  wp_enqueue_style( 'styles-xlarge', get_theme_file_uri('/css/styles-xlarge.min.css'), '', null, '(min-width:1200px)' );
}
add_action( 'wp_enqueue_scripts', 'enqueue_styles_by_media' );

Optimizar la entrega de CSS

Uso del atributo media

Acepta valores de ancho (min-, max-), alto (min-, max-), orientación (portrait, landscape), resolución...

Más información sobre el atributo https://www.w3schools.com/tags/att_link_media.asp

<link rel='stylesheet' id='styles-small-css' href='https://yourdomain.com/wp-content/themes/yourtheme/css/styles-small.min.css' type='text/css' media='(min-width:576px)' />

<link rel='stylesheet' id='styles-medium-css' href='https://yourdomain.com/wp-content/themes/yourtheme/css/styles-medium.min.css' type='text/css' media='(min-width:768px)' />

<link rel='stylesheet' id='styles-large-css' href='https://yourdomain.com/wp-content/themes/yourtheme/css/styles-large.min.css' type='text/css' media='(min-width:992px)' />

<link rel='stylesheet' id='styles-xlarge-css' href='https://yourdomain.com/wp-content/themes/yourtheme/css/styles-xlarge.min.css' type='text/css' media='(min-width:1200px)' />

Optimizar la entrega de CSS

¿Qué hacemos con la hoja de estilos principal?

Tendremos una hoja CSS con estilos que aplicarán en cualquier escenario. Si utilizamos el atributo media="all" o lo dejamos vacío, se detectará como un recurso que bloquea el renderizado.

Google nos aconseja utilizar loadCSS

Cómo hacerlo en WordPress

<html>
  <head>
    <style>
      .blue{color:blue;}
    </style>
  </head>
  <body>
    <div class="blue">
      Hello, world!
    </div>
    <noscript id="deferred-styles">
      <link rel="stylesheet" type="text/css" href="small.css"/>
    </noscript>
    <script>
      var loadDeferredStyles = function() {
        var addStylesNode = document.getElementById("deferred-styles");
        var replacement = document.createElement("div");
        replacement.innerHTML = addStylesNode.textContent;
        document.body.appendChild(replacement)
        addStylesNode.parentElement.removeChild(addStylesNode);
      };
      var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
      if (raf) raf(function() { window.setTimeout(loadDeferredStyles, 0); });
      else window.addEventListener('load', loadDeferredStyles);
    </script>
  </body>
</html>

Quitar el JavaScript que bloquea el renderizado del contenido


Esta regla se activa cuando PageSpeed Insights detecta que en tu código HTML se hace referencia a un archivo JavaScript externo que bloquea el renderizado del contenido de la mitad superior de tu página.

Siempre que se encuentra con una secuencia de comandos, debe detenerse y ejecutarla para poder continuar analizando el código HTML.

Se detecta una etiqueta <script> como bloqueante siempre que:

  • Está en el <head> del documento
  • No posee un atributo defer
  • No posee un atributo async

Quitar el JavaScript que bloquea el renderizado del contenido

Siempre que sea posible, los scripts al footer

Scripts
wp_enqueue_script(
  string $handle,
  string $src = '',
  array $deps = array(),
  string|bool|null $ver = false,
  bool $in_footer = false
);

// Estableciendo el último parámetro a true, se encolará el JS en el footer.

Quitar el JavaScript que bloquea el renderizado del contenido

Forzar los scripts de otros plugins al footer

Otros plugins pueden estar cargando scripts en el <head> de tu sitio. Con esta función podemos mover todos los scripts al hook wp_footer.

// Remove scripts from head.
function move_scripts_from_head_to_footer() {
    remove_action( 'wp_head', 'wp_print_scripts' );
    remove_action( 'wp_head', 'wp_print_head_scripts', 9 );
    remove_action( 'wp_head', 'wp_enqueue_scripts', 1 );
  
    add_action( 'wp_footer', 'wp_print_scripts', 5);
    add_action( 'wp_footer', 'wp_enqueue_scripts', 5);
    add_action( 'wp_footer', 'wp_print_head_scripts', 5);
}
add_action('wp_enqueue_scripts', 'move_scripts_from_head_to_footer');

Quitar el JavaScript que bloquea el renderizado del contenido

Atributo async

async se introduce en HTML5 para indicar que el script debe cargarse de forma asíncrona, sin bloquear la presentación del resto de contenido, y ejecutarse inmediatamente cuando se haya descargado.

El orden de ejecución no se garantiza.

Scripts Async
// Add async attr to scripts.
function add_async_attr_to_all_scripts( $tag ) {
  return str_replace( ' src', ' async="async" src', $tag );
}
add_filter( 'script_loader_tag', 'add_async_attr_to_all_scripts', 10 );

Quitar el JavaScript que bloquea el renderizado del contenido

Atributo defer

defer indica al navegador que continúe con el parseo y presentación del HTML. El script se ejecutará cuando la página se ha terminado de parsear, justo antes de lanzarse el evento DOMContentLoaded.

Además, los scripts con defer deben ejecutarse en el orden en que se especificaron en la página.

Scripts Defer
// Add defer attr to scripts.
function add_defer_attr_to_all_scripts( $tag ) {
  return str_replace( ' src', ' defer="defer" src', $tag );
}
add_filter( 'script_loader_tag', 'add_defer_attr_to_all_scripts', 10 );

Generalizando...

async, defer, inline

¿Cómo lo lleváis?

 

Bola extra: Resource Hints


Los Resource Hints, o precarga de recursos, es una técnica que funciona client-side y que puede tener un impacto importante en WPO, sobretodo en conexiones lentas.

Un resource hints sugiere al navegador que descargue recursos que no van a ser utilizados en ese momento pero que pueden ser utilizados en el futuro.

La descarga se realiza en segundo plano y antes de que el cliente necesite el recurso, de ahí que se hable de "precarga".

Hay varios tipos: dns-prefetch, preconnect, prefetch, prerender... pero vamos a ver el preload.

Resource Hints: preload

Diferentes métodos para hacer preload de un recurso

La directiva preload es obligatoria y de alta prioridad, por lo que su uso está centrado en la navegación actual.

El link preload permite notificar al navegador como debe tratar el recurso precargado a través del atributo as.

Los assets que pueden hacer uso de preload son: audio, video, track, script, style, font, image, fetch, worker, embed, object y document.

La precarga de un recurso puede especificarse a través de marcado HTML (a través de una etiqueta link), vía JavaScript, o mediante una cabecera HTTP.

Más información en W3C

preload mediante marcado HTML (etiqueta link)
<link rel="preload" href="/styles/other.css" as="style">

preload mediante JavaScript
<script>
  var res = document.createElement("link");
  res.rel = "preload";
  res.as = "style";
  res.href = "styles/other.css";
  document.head.appendChild(res);
</script>

preload mediante cabecera HTTP
Link: <https://example.com/other/styles.css>; rel=preload; as=style

Resource Hints: preload

Ejemplo de cómo hacer preload a recursos CSS y JS

Más información en Mozilla.org

<head>
  <meta charset="utf-8">
  <title>JS and CSS preload example</title>

  <link rel="preload" href="style.css" as="style">
  <link rel="preload" href="main.js" as="script">

  <link rel="stylesheet" href="style.css">
</head>

<body>
  <h1>bouncing balls</h1>
  <canvas></canvas>

  <script src="main.js"></script>
</body>

Resource Hints: preload

Añadir la etiqueta preload a los recursos CSS

¡OJO! Si eliminas el parámetro ver de tus recursos, no lo concatenes aquí porque el navegador lo interpretaría como un recurso diferente y no haría preload.

function preload_css() {
  global $wp_styles;

  foreach ( $wp_styles->queue as $handle ) {
    $style  = $wp_styles->registered[ $handle ];
    $source = $style->src . ( $style->ver ? "?ver={$style->ver}" : "" );

    echo '<link rel="preload" href="' . $source . '" as="style">';
  }
}
add_action( 'wp_head', 'preload_css' );

Resource Hints: preload

Añadir la etiqueta preload a los recursos JS

¡OJO! Si eliminas el parámetro ver de tus recursos, no lo concatenes aquí porque el navegador lo interpretaría como un recurso diferente y no haría preload.

function preload_js() {
  global $wp_scripts;

  foreach ( $wp_scripts->queue as $handle ) {
    $script = $wp_scripts->registered[ $handle ];
    $source = $script->src . ( $script->ver ? "?ver={$script->ver}" : "" );

    echo '<link rel="preload" href="' . $source . '" as="script">';
  }
}
add_action( 'wp_head', 'preload_js' );

Resource Hints: preload

Añadir la etiqueta preload a los recursos FONTS

Quizá los preload de mayor repercusión, porque si se encuentran en dentro de hojas de estilo, se procesarán más tarde.

function preload_fonts() {
  echo '<link rel="preload" href="https://yourdomain.com/wp-content/themes/yourtheme/assets/fonts/yourfont.woff" as="font">';

  echo '<link rel="preload" href="https://yourdomain.com/wp-content/themes/yourtheme/assets/fonts/yourfont.eot" as="font">';
}
add_action( 'wp_head', 'preload_fonts' );

Audits

Preload fonts

Resource Hints: preload

Preload a través de una cabecera HTTP

¡OJO! Si eliminas el parámetro ver de tus recursos, no lo concatenes aquí porque el navegador lo interpretaría como un recurso diferente y no haría preload.

function preload_assets() {
  header( 'Link: <https://yourdomain.com/yourcss.css>; rel=preload; as=style', false );

  header( sprintf('Link: <https://yourdomain.com/yourjs.js>; rel=preload; as=script', false );

  header( sprintf('Link: <https://yourdomain.com/yourfont.woff>; rel=preload; as=font; crossorigin', false );
}
add_action( 'send_headers', 'preload_assets' );

HTTP Headers

HTTP Headers

Carga condicional


Menos es más, ¿lo recuerdas?

Gracias a los conditional tags de WordPress podemos hacer carga condicional de recursos

¿Para qué cargar los CSS y JS de Disqus en un listado de categorías o la home? ¿Para qué cargar los CSS y JS de un formulario de contacto en el resto de páginas que no tienen un formulario de contacto?

Carga condicional

No cargues recursos CSS y JS donde no se utilicen

Disponemos de multitud de conditional tags para ejecutar código allí donde sea necesario: is_single(), is_page(), is_home(), is_category()...

Más información aquí

// Disqus sólo en single
function disqus_only_in_single() {
    if ( is_singular( 'post' ) && comments_open() ) {
        return;
    }
        
    wp_dequeue_script( 'disqus_count' );
    wp_dequeue_script( 'disqus_embed' );
}
add_action( 'wp_enqueue_scripts', 'disqus_only_in_single', 100 );

Ya terminamos, lo prometo!

 

CDN


Si utilizamos una CDN, podremos servir nuestros recursos estáticos desde el servidor más cercano al usuario que disponga la CDN. Además, están optimizados.

Además de ganar en velocidad de carga, sobrecargamos menos nuestro servidor.

jQuery sin CDN

assets from server

jQuery con CDN

assets from cdn

¡GRACIAS!