JavaScript

Última revisión: 31 de diciembre de 2021

Sobrecarga de JavaScript

Si el CSS es crítico en cuanto a la carga de un sitio se refiere, el JavaScript lo es aún más. Una primera base para tener presente es hacer que el sitio no dependa de JavaScript al menos hasta que esté cargado, y más si depende de bibliotecas externas como jQuery.

Cuando hablamos de la carga de JavaScript lo primero es separar los distintos elementos y no cargar todo a la vez, ya que se generaría un bloqueo sobre todo el sitio.

Es por esto que la base inicial es cargar las diferentes bibliotecas de elementos que simplemente se carguen, pero sin que se ejecute nada. Todo el JavaScript ha de ejecutarse una vez se haya producido el «Load del DOM». En general este tipo de ficheros, una vez cargados no ejecutan nada, por lo que no bloquean de por sí.

Lo siguiente que haremos es minimizar el contenido de los ficheros de JavaScript, al igual que se hace con los CSS. El objetivo es eliminar bytes que no son útiles y reducir el tiempo de descarga.

Un ejemplo de código podría ser el siguiente:

$(document).ready(function() {
  $("p").click(function() {
    $(this).hide();
  });
});

Que quedaría de la siguiente manera:

$(document).ready(function(){$("p").click(function(){$(this).hide()})});

Otro de los elementos principales hoy en día en JavaScript son los códigos de terceros. Aquí podemos incluir los códigos de Google Analytics, los de botón de compartir en Twitter, o los Likes de Facebook. Estos códigos no los alojamos nosotros, ni los hemos programado nosotros ni marcamos los tiempos nosotros.

Como en general la mayoría de los problemas que aparecen en los datos de WebPageTest o de PageSpeed Insights vienen derivados de estos scripts, hay que plantearse varias opciones con lo que respecta a ellos.

Lo primero que haremos es identificar estos scripts. Por norma general estas herramientas ya te lo permiten ver, ya que suelen ser todos aquellos que hacen llamadas fuera de tu hostname.

Algunas cosas que podemos hacer para mejorar los tiempos de respuesta vienen principalmente en cómo se cargan estos scripts. Para ello tenemos una primera opción que es la de cargar los scripts de forma asíncrona (async) o aplazada (defer). En ocasiones funciona, en ocasiones no. A ser posible lo mejor es cargarlos de manera asíncrona, y si no funciona, aplazada. En caso de no funcionar deberíamos plantear otras estrategias.

<script async src="/script.js">
<script defer src="/script.js">

Por defecto los scripts bloquean la carga del HTML. Esto significa que el HTML no empieza a ser funcional hasta que todo el script está funcionando y activo. En el caso del aplazado lo que se hace es cargar los scripts, y comenzar a ejecutarlos una vez se ha acabado de cargar el HTML, de forma que va por pasos. El caso óptimo es el asíncrono, que en paralelo a la carga del HTML va cargando los scripts sin bloquearse unos a otros.

Otra forma de optimizar la carga de elementos externos, como se ha comentado antes, es la precarga de los elementos con preconnect y prefetch. De esta manera haremos llamadas a elementos que se cargarán en el navegador para que, posteriormente cuando sean necesarios, ya estén disponibles.

Otra opción que puede ser interesante para reducir los tiempos de carga es descargar los scripts de terceros y servirlos directamente desde tu propio servidor, pudiéndoles aplicar sistemas de minimización automática.

El funcionamiento, algo más complejo técnicamente, sería el de:

  • Descargar el fichero JS del servidor original con cierta frecuencia (cada hora, cada día) de forma programada.
  • Optimizar el fichero descargado con sistemas automáticos de compresión y minimizado.
  • Servir el script directamente desde nuestro sitio web y no desde el sitio original.

Posteriormente, serviríamos el script como si de cualquier otro script nuestro se tratase, pudiéndolo cargar de forma asíncrona sin problema.

Optimización general de JavaScript

JavaScript, como cualquier otro lenguaje de programación, tiene sus cosas buenas y malas, pero, sobre todo, al ser un lenguaje que se puede interpretar y que se ejecuta en el navegador cliente, vale la pena aplicar una serie de reglas básicas.

Evitar las variables globales es una idea bastante buena ya que el rendimiento de dichas variables es bastante bajo. En el caso de ejecutar varios códigos es probable que se sobrescriban dichas variables. Para ello podríamos tener elementos de este estilo que se protegen de accesos externos o la posibilidad de ser sobrescritos.

miNameSpace = function() {
  var current = null;
  function init() {...}
  function change() {...}
  return {
    init:init,
    set:change
  }
}();

Otro detalle importante es el uso de un código lo más estricto posible, es decir, un código que se ajuste a cualquier motor que interprete JavaScript y sea fácilmente interpretable. Para verificar que estamos usando un código correcto podemos hacer uso de herramientas como JSLint.

Es mejor no cambiar valores por defecto del DOM en los valores de los elementos (sobre todo en aquellos que hacen referencia a los estilos), sino modificarlos haciendo cambios en clases que se aplican a dichos elementos. Por ejemplo, podemos aplicar unos cambios tales que:

inputs.style.borderColor = '#f00';
inputs.style.borderStyle = 'solid';
inputs.style.borderWidth = '1px';

O podemos hacer un cambio más sencillo, tal que este:

inputs.className += ' error';

Y que esta clase “error” sea la que haga el cambio en el estilo, sin modificar directamente los elementos.

Es mejor utilizar la anotación corta para crear objetos o arrays. De esta forma, un objeto tal que este:

var perro = new Object();
perro.color = 'negro';
perro.tamano = 'grande';

Podría crearse mucho más eficiente así:

var perro = {
  color: 'negro',
  tamano = 'grande'
};

Con un array pasaría lo mismo. Si tenemos algo así:

var series = new Array();
series[0] = 'Fringe';
series[1] = 'The Big Bang Theory';

Sería mucho más eficiente creándose así:

var series = [
  'Fringe',
  'The Big Bang Theory'
];

Otra forma de optimizar código puede ser reduciendo los condicionales. De esta forma un condicional habitual tal que:

var direccion;
if(x > 10) {
  direccion = 1;
} else {
  direccion = -1;
}

Podría llegar a reducirse a un código tal que:

var direccion = (x > 100) ? 1 : -1;

Otra acción sencilla que permite mejorar el rendimiento es el de optimizar los bucles. Un bucle habitual podría ser este:

var beatles = ['George','Ringo','Paul','John'];
for(var i=0; i<beatles.length; i++) {
  Actuan(beatles[i]);
}

Tendría una opción mejorada tal que así, creando las variables sólo una vez:

var beatles = ['George','Ringo','Paul','John'];
for(var i=0, j=beatles.length; i<j; i++) {
  Actuan(beatles[i]);
}

Optimización para jQuery

jQuery es una biblioteca de funciones preestablecidas de JavaScript y que viene incluida en WordPress que permite interactuar de forma más sencilla con los elementos DOM de una página, además de conseguir trabajar de una forma más sencilla gracias a las ampliaciones de código que existen.

En el caso concreto de WordPress, jQuery está pensado para ser utilizado en el panel de administración y no como herramienta para el frontal, como se puede ver en el resumen del impacto de jQuery en los temas.

Existen algunas reglas que permiten trabajar de forma más rápida y mejorada con jQuery:

La forma más rápida de acceder a un elemento del DOM es hacerlo mediante un selector ID en vez de hacerlo con un descendiente suyo. Esto es debido a la existencia y uso de la función getElementById(). Esto significa que es óptimo hacer uso de algo así:

var boton1 = $('#boton1');

que no hacer uso de algo así:

var boton1 = $('#botones .boton1');

La siguiente forma de acceder rápidamente a un elemento es hacerlo a través de un “tag” ya que se aprovecha el uso de la función getElementsByTagName().

Otro detalle a tener en cuenta es el uso de variables para almacenar información de un elemento y no aplicar cambios directamente sobre él. De esta forma tendríamos una función optimizada tal que así:

var $activar = $('#light input.on');
$activar.css('border', '3px dashed yellow');
$activar.css('background-color', 'orange');
$activar.fadeIn('slow');

Sobre unas no optimizadas que podrían ser así:

$('#light input.on).css('border', '3px dashed yellow');
$('#light input.on).css('background-color', 'orange');
$('#light input.on).fadeIn('slow');

Este sistema podría mejorar incluso un poco más haciendo uso de encadenamiento:

var $activar = $('#light input.on');
$activar.css('border', '3px dashed yellow').css('background-color', 'orange').fadeIn('slow');

Otra forma de acceder rápidamente a subelementos es utilizar la función find().

var $activar = $('#light'), $activo = $activar.find('input.on'), $inactivo = $activar.find('input.off');

Los eventos son uno de los elementos que más se utilizan en jQuery y debemos aprovechar la delegación de estos para generar el llamado Bubbling. Por ejemplo, podemos tener una función sencilla como:

$('#lista li).bind('click', function() {
  $(this).addClass('clicked');
});

Esto implica tener que trabajar enormemente con el DOM, algo que podríamos reducir utilizando una función algo más compleja aparentemente, pero más efectiva:

$('#lista).bind('click', function(e) {
  var pulsa = e. pulsa, $ pulsa = $( pulsa);
  if (pulsa.nodeName === 'LI') {
    $pulsa.addClass('clicked');
  }
});

Cargar las funciones tras la carga de la ventana y no del estado del documento. Por norma general las funciones se cargan tras el $(document).ready pero se pueden cargar de una forma más eficiente con $(window).load.

El JavaScript en WordPress

WordPress, como gestor de contenidos, está formado por dos partes, la del frontal y la del panel de administración. El sistema tiene sus propios controles para la gestión de los JavaScript y estos sistemas siempre han de estar en el tema, si queremos obtener una optimización fiable y segura. Si un tema está mal desarrollado o no sigue los estándares de WordPress, es probable que no puedas optimizar correctamente esta parte o genere errores.

En este proceso deberíamos conseguir 4 pasos. El primero de ellos es eliminar las cadenas de versión que muchas veces llevan las peticiones CSS (?ver=5.8.2). El segundo sería el de reducir la cantidad de peticiones a ficheros JavaScript, para conseguir una o dos peticiones. A partir de aquí sería conveniente hacer un minify del contenido, para reducir su tamaño y, finalmente, conseguir que ese fichero JS resultante quede cacheado para siguientes llamadas.

En general hay muchos plugins que hacen esta tarea, pero son un «todo en uno», es decir, no están pensados solo para esta acción, sino que intervienen otras optimizaciones. Por eso es importante elegir un plugin correctamente y que no afecte a otras configuraciones.