Minimizar estáticos (CSS y Javascript) en WordPress con YUI Compressor

Seguro que ya estás aburrido de escuchar lo importantísimo que es la velocidad de una web para la experiencia de usuario. No obstante, por falta de tiempo o ganas, nunca te has puesto a implementar todas las sugerencias propuestas por Google PageSpeed: Reduce el tamaño de las imágenes, especifica la caché de navegador, minimiza y concatena tu CSS y Javascript

4520986339_99857d1c35_o

Precisamente este último punto es el que voy a tratar en este artículo. La minificación consiste en eliminar los espacios en blanco y saltos de linea de los archivos CSS y Javascript. Con esto conseguiremos una reducción del tamaño de los archivos, ahorrando en ancho de banda y acelerando la descarga de los mismos. Por otro lado, concatenar consiste básicamente en agrupar multiples archivos en uno solo. Esto sirve para ahorrar peticiones al servidor, algo también muy importante cuando un cliente utiliza una conexión lenta, porque reduce el número de conexiones necesarias para cargar una web, limitando la latencia producida por la creación de nuevas peticiones al servidor.

En este artículo voy a explicar la técnica que empleamos en TNW para minimizar y concatenar CSS y Javascript – Probablemente no sea la mejor ni la más eficiente en términos generales, pero es la que mejor nos funciona teniendo en cuenta nuestros requisitos técnicos. Voy a asumir que tenemos un blog en WordPress, con entornos diferentes de desarrollo y producción, y que hacemos deployments usando Capistrano.

1.Instalar YUI Compressor

YUI Compressor es una librería open source creada para minimizar de forma segura Javascript y CSS. Si utilizas Ubuntu en una de sus últimas versiones, es facilísimo de instalar, con todas sus dependencias (Java, etc) incluídas:

apt-get install yui-compressor

Una vez instalado en nuestro servidor de producción, podemos usarlo de la siguiente manera (El parámetro -o sirve para indicar el nombre del archivo de salida:

yui-compressor script.js -o script-min.js --charset utf-8

2. Automatizar con Capistrano

Ahora vamos a automatizar la llamada a YUI Compressor, incluyéndola en nuestro script de deploy de Capistrano para minimizar automáticamente los archivos en cada deploy. Además, incluiremos una pequeña función que concatene los archivos minimizados. Este script puede ser parecido a este:

#!/usr/bin/ruby

namespace :minifier do

  # Set global variables and define the list of files to concatenate
  task :compress do

    set :assets, "#{current_path}/path/to/assets/folder/"
    set :cmd, "yui-compressor --charset utf-8"
    minify([
      "#{assets}/js/jquery",
      "#{assets}/js/library_1",
      "#{assets}/js/library_2",
    ], "js")

  end

  def minify(files, ext)

    # Minimize each file using YUI Compressor
    files.each do |file|
      file = "#{file}"
      if File.exists?("#{file}.#{ext}")
        begin
          run "#{cmd} #{file}.#{ext} -o #{file}.min.#{ext}"
        rescue
          raise CommandError.new("Error while minifying file #{file}")
        end
      else
      	raise CommandError.new("#{file}.#{ext} does not exists")
      end
    end

    # Concatenate all files
    files.join
    files = files.map { |file| file + ".min.#{ext}" }.join(" ")
    run "cat #{files} > #{assets}/#{ext}/scripts.min.#{ext}"
  end

end

A continuación, podemos incluir este script en nuestro Capfile e invocar la función en una vez el deploy haya finalizado:

# Incluir funciones de minificacion
load 'capistrano/lib/minify'
after "deploy", "minifier:compress"

3. Modificar WordPress para usar los archivos en producción

Por último, una vez tenemos los archivos fuente minificados y concatenados, solo nos queda hacer referencia a ellos desde el código fuente de nuestro sitio de WordPress.

<?php 
if( !isset( $_SERVER['ENVIRONMENT'] ) == 'production' ) { 
    echo '<script src="'.version( '/path/to/assets/folder/js/scripts.min.js').'" type="text/javascript"></script>';
} else {
    echo '<script src="'.version( '/path/to/assets/folder/js/jquery.js' ).'" type="text/javascript"></script>';
    echo '<script src="'.version( '/path/to/assets/folder/js/libraries.js' ).'" type="text/javascript"></script>';
    echo '<script src="'.version( '/path/to/assets/folder/js/scripts.js' ).'" type="text/javascript"></script>';
}

function version( $path ) {
    return "http://". $_SERVER['HTTP_HOST'].$path."?".filemtime( $_SERVER['DOCUMENT_ROOT'] . $path );
}
?>

Nota: Este script no emplea la función nativa de WordPress  para incluir scripts y hojas de estilo (wp_enqueue_script y wp_enqueue_style) ya que personalmente me gusta más hacer referencia a ellos directamente en el código sin necesidad de llamar otras funciones. Sin embargo, sospecho que esta técnica debería funcionar más o menos igual para los que prefieran hacer uso de las funciones de WP.

Este código comprueba la variable del servidor llamada ENVIRONMENT para decidir si debe incluir el archivo concatenado (en producción), o los archivos fuente originales (en desarrollo). Además, a la URL del archivo se le añade un timestamp con la fecha de modificación para indicar al navegador si el archivo ha cambiado y, en caso afirmativo, debe pedirlo de nuevo al servidor.

Bola extra: Si usamos Varnish u algún otro proxy reverso para cachear nuestro sitio que soporte Edge Side Includes (ESI), podemos poner el código anterior en un archivo aparte e incluirlo mediante ESI en nuestro sitio. Esto tiene la ventaja de permitir modificar en cada deploy y que aparezca, sin necesidad de purgar toda nuestra caché para que el servidor sirva la versión actualizada de nuestros CSS.

Resultados

Todavía estamos trabajando para implementar esta técnica al 100% en The Next Web pero los primeros resultados son prometedores: Sólo comprimiendo archivos de Javascript, hemos reducido su tamaño aproximadamente un 10%, esto supone unos 5KB por visita. Multiplicado por 14 millones de paginas vistas al mes, (y corrigiendo muy a la baja ya que gran mayoría de las peticiones estarán cacheadas), estaremos ahorrando del orden de decenas de GB en transferencia de datos, lo cual no es moco de pavo.

Terminaré diciendo que cada maestrillo tiene su librillo – Esta configuración en concreto puede no adaptarse a todas las situaciones, pero espero que te sirva como inspiración y te anime a encontrar soluciones adecuadas para tu propia web.

Lee ahora: Demo CSS3: Un diseño con medidas basadas en viewport »