Cómo medir las métricas de rendimiento críticas con Google Analytics

En este codelab, aprenderás a usar Google Analytics y la API de User Timings para medir el rendimiento real de tu sitio web o aplicación, y optimizarlo para mejorar la experiencia de tus usuarios.

Las herramientas como WebPagetest.org son un excelente punto de partida para las optimizaciones de rendimiento, pero la verdadera prueba del rendimiento del sitio siempre serán los datos reales de los usuarios reales.

Si administras un sitio web, es muy probable que ya uses Google Analytics para medir el tráfico y otros aspectos, como el uso de dispositivos y navegadores. Con un poco de código adicional, puedes agregar métricas de rendimiento a la combinación.

Qué aprenderás

  • Cómo medir de forma precisa y eficaz las métricas de rendimiento con la API de User Timings
  • Cómo enviar esos datos a Google Analytics para que se puedan incorporar a tus informes

Requisitos

¿Cómo usarás este instructivo?

Ler Leer y completar los ejercicios

¿Cómo calificarías tu experiencia con la compilación de sitios o aplicaciones web?

Principiante Intermedio Avanzado

Presiona el siguiente botón para descargar el código de muestra completo en tu computadora:

Download Zip

… o clona el repositorio de GitHub desde la línea de comandos.

git clone https://github.com/googlecodelabs/performance-analytics.git

El código de muestra se divide en subdirectorios que corresponden a cada uno de los pasos numerados de este codelab. Puedes usarlo para navegar fácilmente por el codelab o verificar que tu implementación sea correcta.

Si tienes acceso a un programa de comparación de diferencias, puedes usarlo para ver exactamente qué cambió de un paso a otro.

En este codelab, tomarás un solo archivo HTML que carga los siguientes recursos:

  • Fuentes web
  • Hojas de estilo
  • Imágenes
  • JavaScript

Escribirás un código nuevo que mida las métricas clave de rendimiento para cada uno de estos tipos de recursos.

Consideraciones sobre el rendimiento de los recursos

Si alguna vez leíste algo sobre la optimización del rendimiento, probablemente ya sepas que cada uno de estos tipos de recursos tiene sus propias peculiaridades y puede afectar el rendimiento percibido general de varias maneras.

CSS

Por ejemplo, las hojas de estilo bloquean la renderización de todos los elementos del DOM que vienen después de ellas, lo que significa que el navegador debe realizar una solicitud para la hoja de estilo, descargarla y analizarla antes de poder renderizar cualquier contenido del DOM que venga después de ella. Por este motivo, suele ser mejor colocar las hojas de estilo en el <head> del documento. Además, debido a la naturaleza de bloqueo del CSS, también se recomienda colocar solo el CSS crítico en <head> y cargar el CSS no crítico de forma asíncrona después.

JavaScript

Por otro lado, JavaScript no bloquea el procesamiento, pero sí bloquea el análisis y la construcción del DOM. Esto es necesario porque JavaScript puede modificar el DOM, lo que significa que, cada vez que el navegador ve una etiqueta <script> (excepto las secuencias de comandos asíncronas), debe ejecutar el código antes de continuar con la siguiente etiqueta. Si la etiqueta <script> hace referencia a un archivo JavaScript externo, debe descargar y ejecutar el código antes de continuar.

Por este motivo, a menudo se recomienda que tu código JavaScript se cargue justo antes de la etiqueta de cierre </body>, de modo que la mayor parte del DOM esté disponible lo más rápido posible.

Fuentes web

Si cargas fuentes web, también puedes optar por bloquear la renderización del documento hasta que la fuente esté disponible para su uso. En este caso, es fundamental comprender cuánto tiempo les lleva a tus usuarios. Es posible que una fuente web se cargue rápidamente para ti, pero muy lentamente para la mayoría de las personas que visitan tu sitio. Por eso, es tan importante realizar mediciones y tomar decisiones basadas en datos reales.

Imágenes

Por último, las imágenes pueden darle vida a un sitio, pero también pueden tardar más en cargarse. Comprender lo que esto significa realmente y poder detectar cualquier correlación entre los patrones de uso y los tiempos de carga de la página es fundamental para saber cómo realizar optimizaciones.

El primer paso en este lab de código es ver cómo se ve la página de demostración antes de agregar cualquier código de medición del rendimiento.

Para ver la demostración, crea una carpeta nueva y agrégale un archivo llamado index.html. Luego, copia y pega el siguiente código en el archivo index.html.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Performance Analytics Demo</title>

  <!-- Start fonts -->
  <link href="https://fonts.googleapis.com/css?family=Roboto:400,700,400italic" rel="stylesheet">
  <!-- End fonts -->

  <!-- Start CSS -->
  <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
  <style>
    body { font-family: Roboto, sans-serif; margin: 1em; }
    img { float: left; height: auto; width: 33.33%; }
    .gallery { overflow: hidden; }
  </style>
  <!-- End CSS -->

</head>
<body>

  <div class="container">

    <!-- Start images -->
    <div class="gallery">
      <img src="http://lorempixel.com/380/200/animals/1/">
      <img src="http://lorempixel.com/380/200/animals/2/">
      <img src="http://lorempixel.com/380/200/animals/3/">
    </div>
    <!-- End images -->

    <h1>Performance Analytics Demo</h1>
    <p>Real performance data from real users.</p>

  </div>

  <!-- Start JavaScript -->
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
  <!-- End JavaScript -->

</body>
</html>

A continuación, abre Web Server for Chrome y, luego, inicia un servidor local en el directorio que acabas de crear. Asegúrate de que la opción "Mostrar automáticamente index.html" esté marcada.

Screen Shot 2016-05-11 at 1.03.43 PM.png

Ahora deberías poder navegar a http://127.0.0.1:8887/ en tu navegador y ver el archivo de demostración. Debería verse algo similar a esto:

Screen Shot 2016-05-11 at 10.59.03 AM.png

Una vez que se ejecute la página de demostración, tómate un momento para observar el código y ver todos los tipos de recursos que se cargan. En los próximos pasos, agregarás código para medir cuándo se cargan estos recursos y el usuario puede interactuar con ellos.

Como se mencionó anteriormente en la sección de consideraciones sobre el rendimiento de los recursos, CSS bloquea la renderización de los elementos DOM, así como la ejecución de las secuencias de comandos que le siguen en el DOM.

El archivo de demostración que acabas de crear contiene el siguiente CSS, que hace referencia a Bootstrap y a algunos estilos intercalados.

<!-- Start CSS -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
<style>
  body { font-family: Roboto, sans-serif; margin: 1em; }
  img { float: left; height: auto; width: 33.33%; }
  .gallery { overflow: hidden; }
</style>
<!-- End CSS -->

Dado que CSS bloquea la renderización de elementos DOM y la ejecución de secuencias de comandos, es posible determinar cuándo CSS deja de bloquear agregando una etiqueta <script> inmediatamente después del CSS que almacena la hora actual.

Podrías hacerlo creando una variable y asignándole new Date(), pero, gracias a la API de User Timings, existe una forma mucho más sencilla: el método performance.mark.

Para marcar el momento en que el CSS termina de bloquear la renderización y la ejecución de la secuencia de comandos, agrega la siguiente línea de código inmediatamente antes del comentario de cierre <!-- End CSS -->.

<script>performance.mark('css:unblock');</script>

El método performance.mark crea una marca de tiempo de alta resolución en este punto exacto y la asocia con el nombre que se pasó al método. En este caso, llamaste a la marca "css:unblock".

El método performance.mark va de la mano con el método performance.measure, que se usa para calcular la diferencia de tiempo entre dos marcas (además de las marcas que creas, también puedes usar las marcas que el navegador crea automáticamente para los distintos puntos de la API de Navigation Timing).

La siguiente función de utilidad mide y devuelve la duración entre una marca que agregaste y la marca responseEnd creada por la API de Navigation Timing.

function measureDuration(mark, opt_reference) {
  var reference = opt_reference || 'responseEnd';
  var name = reference + ':' + mark;

  // Clears any existing measurements with the same name.
  performance.clearMeasures(name);

  // Creates a new measurement from the reference point to the specified mark.
  // If more than one mark with this name exists, the most recent one is used.
  performance.measure(name, reference, mark);

  // Gets the value of the measurement just created.
  var measure = performance.getEntriesByName(name)[0];

  // Returns the measure duration.
  return measure.duration;
}

Para comenzar a usar esta función de utilidad, crea un archivo nuevo llamado perf-analytics.js (en el mismo directorio que el archivo index.html) y copia y pega el código anterior en él.

Ahora que esta función está definida, puedes llamarla y pasarle el nombre de la marca "css:unblock". Para no interferir en la carga de ningún otro recurso, debes aplazar la ejecución de estas mediciones hasta después de que se active el evento de carga de la ventana.

Una vez que hayas escrito una función para llamar a este código, tu archivo perf-analytics.js debería verse de la siguiente manera:

window.onload = function() {
  measureCssUnblockTime();
};


/**
 * Calculates the time duration between the responseEnd timing event and when
 * the CSS stops blocking rendering, then logs that value to the console.
 */
function measureCssUnblockTime() {
  console.log('CSS', 'unblock', measureDuration('css:unblock'));
}


/**
 * Accepts a mark name and an optional reference point in the navigation timing
 * API and returns the time duration between the reference point and the last
 * mark (chronologically).
 * @param {string} mark The mark name.
 * @param {string=} opt_reference An optional reference point from the
 *     navigation timing API. Defaults to 'responseEnd'.
 * @return {number} The time duration
 */
function measureDuration(mark, opt_reference) {
  var reference = opt_reference || 'responseEnd';
  var name = reference + ':' + mark;

  // Clears any existing measurements with the same name.
  performance.clearMeasures(name);

  // Creates a new measurement from the reference point to the specified mark.
  // If more than one mark with this name exists, the most recent one is used.
  performance.measure(name, reference, mark);

  // Gets the value of the measurement just created.
  var measure = performance.getEntriesByName(name)[0];

  // Returns the measure duration.
  return measure.duration;
}

Por último, deberás cargar la secuencia de comandos perf-analytics.js desde index.html. Para ello, agrega la siguiente etiqueta de secuencia de comandos a tu documento principal. Asegúrate de agregarlo al final para que no interfiera en la carga de otros recursos.

<!-- Start performance analytics -->
<script async src="perf-analytics.js"></script>
<!-- End performance analytics -->

Cuando completes este paso, tu código debería coincidir con el contenido del directorio 01-css del repositorio del codelab.

Si cargas la página en un navegador y abres la consola para desarrolladores, deberías ver un resultado similar al siguiente:

Screen Shot 2016-05-17 at 11.13.02 AM.png

Por lo general, las fuentes web se cargan a través de una hoja de estilo externa, como se muestra en el archivo de demostración inicial:

<!-- Start fonts -->
<link href="https://fonts.googleapis.com/css?family=Roboto:400,700,400italic" rel="stylesheet">
<!-- End fonts -->

Dado que se trata de una etiqueta <link> para un archivo CSS, podría parecer que determinar cuándo se cargan las fuentes y están listas para usarse es tan simple como agregar una marca dentro de una etiqueta <script> inmediatamente después de la <link>, al igual que en el paso 1.

Lamentablemente, no es tan simple.

Las hojas de estilo bloquean la ejecución de JavaScript porque el contenido de las hojas de estilo se usa para construir el CSSOM y, dado que es posible que el JavaScript que se está cargando necesite acceder al CSSOM, la ejecución debe retrasarse hasta que el CSSOM esté completamente construido.

El problema es que el navegador puede construir el CSSOM sin descargar la fuente, lo que significa que, si agregas una marca a través de una etiqueta de secuencia de comandos intercalada en el DOM inmediatamente después de la etiqueta <link> de la hoja de diseño de la fuente, es probable que la marca se produzca antes de que la fuente se cargue por completo.

Hasta que los eventos de carga de fuentes estén disponibles en los navegadores, se necesita JavaScript para determinar cuándo una fuente está realmente activa y lista para usarse en la página. Afortunadamente, cargar fuentes a través de JavaScript también mejora el rendimiento, ya que no requiere una solicitud de bloqueo adicional a un archivo CSS.

La mayoría de las fuentes web (incluidas las fuentes de Google, Typekit y font.com) se pueden cargar a través de la secuencia de comandos webfont.js, que Google y Typekit desarrollaron en conjunto.

Para actualizar el documento principal y usar webfont.js para cargar las fuentes (en lugar de la etiqueta <link>), reemplaza la sección de fuentes del código por lo siguiente:

<!-- Start fonts -->
<script>
window.WebFontConfig = {
  google: {families: ['Roboto:400,700,400italic']},
  timeout: 10000,
  active: function() {
    performance.mark('fonts:active');
  }
};
</script>
<script async src="https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js"></script>
<!-- End fonts -->

Hay dos aspectos importantes que debes tener en cuenta sobre el código anterior:

  • Crea una marca "fonts:active" en la devolución de llamada activa, de modo que puedas medir más adelante cuánto tardaron en cargarse las fuentes.
  • La etiqueta <script> que carga webfonts.js contiene el atributo async, por lo que no bloqueará el análisis ni la renderización del resto del documento (lo que no sucede con las etiquetas <link>).

Si bien el código anterior crea una marca "fonts:active", medir esta marca y registrarla en la consola no es tan simple como lo era para la marca "css:unblock". El motivo es que la carga de fuentes ahora se realiza de forma asíncrona, por lo que, si intentas medir la marca "fonts:active" en el controlador window.onload (como lo hiciste con "css:unblock"), es muy posible que la fuente aún no se haya cargado.

Para resolver este problema, puedes crear una promesa que se resuelva una vez que se cargue la fuente. La siguiente función lo hace por ti. Cópialo y pégalo en el archivo perf-analytics.js:

/**
 * Creates a promise that is resolved once the web fonts are fully load or
 * is reject if the fonts fail to load. The resolved callback calculates the
 * time duration between the responseEnd timing event and when the web fonts
 * are downloaded and active. If an error occurs loading the font, this fact
 * is logged to the console.
 */
function measureWebfontPerfAndFailures() {
  new Promise(function(resolve, reject) {
    // The classes `wf-active` or `wf-inactive` are added to the <html>
    // element once the fonts are loaded (or error).
    var loaded = /wf-(in)?active/.exec(document.documentElement.className);
    var success = loaded && !loaded[1]; // No "in" in the capture group.
    // If the fonts are already done loading, resolve immediately.
    // Otherwise resolve/reject in the active/inactive callbacks, respectively.
    if (loaded) {
      success ? resolve() : reject();
    }
    else {
      var originalAciveCallback = WebFontConfig.active;
      WebFontConfig.inactive = reject;
      WebFontConfig.active = function() {
        originalAciveCallback();
        resolve();
      };
      // In case the webfont.js script fails to load, always reject the
      // promise after the timeout amount.
      setTimeout(reject, WebFontConfig.timeout);
    }
  })
  .then(function() {
    console.log('Fonts', 'active', measureDuration('fonts:active'));
  })
  .catch(function() {
    console.error('Error loading web fonts')
  });
}

También actualiza el controlador window.onload para que llame a esta nueva función.

window.onload = function() {
  measureCssUnblockTime();
  measureWebfontPerfAndFailures();
};

Cuando completes este paso, tu código debería coincidir con el contenido del directorio 02-fonts del repositorio del codelab.

Si cargas la página en un navegador y abres la consola para desarrolladores, deberías ver un resultado similar al siguiente:

Screen Shot 2016-05-17 at 11.13.22 AM.png

Saber cuándo una imagen es visible no es tan simple como parece. En los pasos anteriores, aprendiste que las hojas de estilo y las etiquetas <script> síncronas pueden bloquear el procesamiento, el análisis y la ejecución de secuencias de comandos. Lo que quizás no sepas es que ninguno de ellos bloquea el escáner de precarga del navegador.

Todos los navegadores modernos implementan un escáner de precarga como uno de los muchos intentos para mejorar el rendimiento, incluso en los sitios que no se enfocan en el rendimiento y que contienen muchos recursos de bloqueo. La idea es que, si bien algunos recursos pueden bloquear el análisis, la renderización o la ejecución de secuencias de comandos, no tienen que bloquear las descargas. Por lo tanto, el navegador analiza el archivo HTML antes de comenzar a construir el DOM y busca recursos que pueda comenzar a descargar de inmediato.

En cuanto a las imágenes, esto significa que es muy probable que ya se hayan descargado cuando se agreguen al DOM. Esto también significa que el punto en el que se descarga una imagen no es necesariamente una buena métrica de rendimiento. La métrica de rendimiento que debes tener en cuenta es el punto en el que una imagen es visible para el usuario.

Cuando se descarga una imagen antes de agregarla al DOM, el punto en el que se hace visible es el punto en el que se encuentra en el DOM. Por otro lado, si una imagen no se descarga antes de agregarse al DOM, el punto en el que se hace visible es cuando se activa su controlador onload.

Por lo tanto, para saber cuándo una imagen es visible, debes controlar ambos casos.

Para ello, agrega marcas en el controlador onload de cada imagen, así como en una etiqueta <script> intercalada inmediatamente después de la última imagen del DOM. La idea es que la marca que se produce al final será la que represente el momento en que todas las imágenes son visibles.

Para agregar marcas para ambos casos (cuando se cargan las imágenes y cuando se renderizan), actualiza el código de las imágenes en index.html de la siguiente manera:

<!-- Start images -->
<div class="gallery">
  <img onload="performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/1/">
  <img onload="performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/2/">
  <img onload="performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/3/">
</div>
<script>performance.mark('img:visible')</script>
<!-- End images -->

Dado que el método performance.measure para un nombre de marca en particular siempre usará la última marca (si encuentra varias marcas con el mismo nombre), la función de utilidad measureDuration en el archivo perf-analytics.js se puede usar para esto sin ninguna modificación adicional:

/**
 * Calculates the time duration between the responseEnd timing event and when
 * all images are loaded and visible on the page, then logs that value to the
 * console.
 */
function measureImagesVisibleTime() {
  console.log('Images', 'visible', measureDuration('img:visible'));
}

Agrega la función anterior al archivo perf-analytics.js y, luego, actualiza el controlador window.onload para llamarla:

window.onload = function() {
  measureCssBlockTime();
  measureWebfontPerfAndFailures();
  measureImagesVisibleTime();
};

Cuando completes este paso, tu código debería coincidir con el contenido del directorio 03-images del repositorio del codelab.

Si cargas la página en un navegador y abres la consola para desarrolladores, deberías ver un resultado similar al siguiente:

Screen Shot 2016-05-17 at 11.13.39 AM.png

Dado que las etiquetas <script> sin el atributo async bloquean el análisis del DOM hasta que se descargan y ejecutan, puedes determinar el punto en el que todas las secuencias de comandos terminaron de ejecutarse agregando una marca en una etiqueta de secuencia de comandos intercalada inmediatamente después del último <script> síncrono en el DOM.

Ten en cuenta que, en este caso, no funcionará el uso de controladores onload, ya que el navegador debe ejecutar la secuencia de comandos después de cargarla, lo que lleva tiempo. Una secuencia de comandos que se carga rápido, pero se ejecuta con lentitud puede ser tan mala como una secuencia de comandos de carga lenta.

Para hacer un seguimiento de cuándo se carga y ejecuta todo el código JavaScript en el documento principal, actualiza la sección de JavaScript en index.html con el siguiente código:

<!-- Start JavaScript -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script>performance.mark('js:execute');</script>
<!-- End JavaScript -->

Esto agregará una marca llamada "js:execute" inmediatamente después de que se descarguen y finalicen la ejecución las secuencias de comandos de jQuery y los complementos de Bootstrap.

Para medir cuánto tiempo lleva esto, agrega la siguiente función a perf-analytics.js:

/**
 * Calculates the time duration between the responseEnd timing event and when
 * all synchronous JavaScript files have been downloaded and executed, then
 * logs that value to the console.
 */
function measureJavaSciptExecutionTime() {
  console.log('JavaScript', 'execute', measureDuration('js:execute'));
}

Luego, invócalo desde el controlador window.onload:

window.onload = function() {
  measureCssBlockTime();
  measureWebfontPerfAndFailures();
  measureImagesVisibleTime();
  measureJavaSciptExecutionTime();
};

Cuando completes este paso, tu código debería coincidir con el contenido del directorio 04-javascript del repositorio del codelab.

Si cargas la página en un navegador y abres la consola para desarrolladores, deberías ver un resultado similar al siguiente:

Screen Shot 2016-05-17 at 11.14.03 AM.png

No todos los navegadores admiten las promesas de JavaScript ni las APIs de User Timing, y si ejecutas el código que escribiste hasta ahora en un navegador que no admite una de estas funciones, recibirás errores.

Para solucionar este problema, puedes usar la detección de funciones. Agrega el siguiente fragmento de código inmediatamente antes de la sección de fuentes. Esta línea de JavaScript detecta la compatibilidad con el método performance.mark, por lo que se debe agregar a la página antes de que se use ese método:

<!-- Start feature detects -->
<script>window.__perf = window.performance && performance.mark;</script>
<!-- End feature detects -->

Luego, en cualquier lugar de index.html en el que llames a performance.mark, antepón la detección de funciones. A continuación, se muestra un ejemplo que actualiza el código en el bloque de imagen, pero debes asegurarte de actualizar las otras secciones también.

<!-- Start images -->
<div class="gallery">
  <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/1/">
  <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/2/">
  <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/3/">
</div>
<script>__perf && performance.mark('img:visible')</script>
<!-- End images -->

Ahora que la variable __perf está configurada en window, también puedes usarla en perf-analytics.js para asegurarte de no llamar a un método que no sea compatible con el navegador actual.

Se debe actualizar la función measureDuration para agregar la siguiente condición:

function measureDuration(mark, opt_reference) {
  if (window.__perf) {
    // ...
  }
}

Por último, dado que no todos los navegadores admiten promesas de JavaScript, la función measureWebfontPerfAndFailures también debe incluirse en una condición:

function measureWebfontPerfAndFailures() {
  if (window.Promise) {
    // ...
  }
}

Ahora deberías poder ejecutar tu código en cualquier navegador sin problemas.

Cuando completes este paso, tu código debería coincidir con el contenido del directorio 05-feature-detects del repositorio del codelab.

El paso final de este codelab es tomar los datos que se registran en la consola y, en su lugar, enviarlos a Google Analytics. Además, antes de enviar datos a Google Analytics, debes agregar la biblioteca analytics.js y el fragmento de seguimiento predeterminado a tu página.

Agrega el siguiente código a index.html después del bloque principal de JavaScript, pero antes de que se cargue la secuencia de comandos perf-analytics.js:

<!-- Start analytics tracking snippet -->
<script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');
</script>
<script async src="https://www.google-analytics.com/analytics_debug.js"></script>
<!-- End analytics tracking snippet -->

Si ya agregaste Google Analytics a un sitio web, sabrás que debes reemplazar el marcador de posición "UA-XXXXX-Y" por el ID de seguimiento que recibiste cuando creaste una propiedad nueva en Google Analytics.

El fragmento de seguimiento analytics.js realiza cuatro tareas principales:

  • Crea un elemento <script> asíncrono que descarga la biblioteca de JavaScript analytics.js.
  • Inicializa una función global ga() (llamada cola de comandos ga()) que te permite programar comandos para que se ejecuten una vez que se cargue la biblioteca analytics.js y esté lista para usarse.
  • Agrega un comando a la cola de comandos ga() para crear un objeto de seguimiento nuevo para la propiedad especificada a través del parámetro "UA-XXXXX-Y".
  • Agrega otro comando a la cola de comandos ga() para enviar una vista de página a Google Analytics para la página actual.

Si bien los datos recopilados solo a partir de las vistas de página son útiles, no cuentan toda la historia. Para obtener una mejor perspectiva de la experiencia de los usuarios en tu sitio o aplicación, debes enviar datos de interacción adicionales a Google Analytics.

Google Analytics admite varios tipos de datos de interacción: vistas de página, eventos, interacciones sociales, excepciones y (por último, pero no menos importante) tiempos de usuario. Para enviar datos de la medición del tiempo del usuario a Google Analytics, puedes usar la siguiente firma de comando:

ga('send', 'timing', timingCategory, timingVar, timingValue);

Donde timingCategory es una cadena que te permite organizar los hits de sincronización en grupos lógicos, timingVar es la variable que estás midiendo y timingValue es la duración real en milisegundos.

Para ver cómo funciona esto en la práctica, la instrucción console.log en la función measureCssUnblockTime se puede actualizar de la siguiente manera:

ga('send', 'timing', 'CSS', 'unblock', measureDuration('css:unblock'));

Si bien el código anterior funcionará en algunas situaciones, hay dos aspectos importantes que debes tener en cuenta:

  • En el paso anterior, se actualizó la función measureDuration para que solo se ejecute si el navegador admite la API de User Timings, lo que significa que, a veces, devolverá undefined. Dado que no hay motivos para enviar datos indefinidos a Google Analytics (en algunos casos, incluso podrían arruinar tus informes), solo debes enviar este hit de tiempo si measureDuration devuelve un valor.
  • Cuando measureDuration devuelve un valor, es un DOMHighResTimeStamp, que tendrá una precisión mayor que la de milisegundos. Dado que timingValue en Google Analytics debe ser un número entero, debes redondear el valor que devuelve measureDuration.

Para tener en cuenta estos detalles, actualiza la sentencia return en la función measureDuration para redondear el valor que se muestra:

function measureDuration(mark, opt_reference) {
  if (window.__perf) {
    // ...
    return Math.round(measure.duration);
  }
}

Además, actualiza los comandos de sincronización para que solo se ejecuten si existe un valor para la métrica en cuestión. Como ejemplo, la función measureCssUnblockTime debería actualizarse a algo similar a lo siguiente:

function measureCssUnblockTime() {
  var cssUnblockTime = measureDuration('css:unblock');
  if (cssUnblockTime) {
    ga('send', 'timing', 'CSS', 'unblock', cssUnblockTime);
  }
}

Deberás realizar actualizaciones similares en todas las demás funciones de medición. Una vez completado, el archivo perf-analytics.js final debería verse de la siguiente manera:

window.onload = function() {
  measureCssUnblockTime();
  measureWebfontPerfAndFailures();
  measureImagesVisibleTime();
  measureJavaSciptExecutionTime();
};


/**
 * Calculates the time duration between the responseEnd timing event and when
 * the CSS stops blocking rendering, then sends this measurement to Google
 * Analytics via a timing hit.
 */
function measureCssUnblockTime() {
  var cssUnblockTime = measureDuration('css:unblock');
  if (cssUnblockTime) {
    ga('send', 'timing', 'CSS', 'unblock', cssUnblockTime);
  }
}


/**
 * Calculates the time duration between the responseEnd timing event and when
 * the web fonts are downloaded and active, then sends this measurement to
 * Google Analytics via a timing hit. If an error occurs loading the font, an
 * error event is sent to Google Analytics.
 */
function measureWebfontPerfAndFailures() {
  if (window.Promise) {
    new Promise(function(resolve, reject) {
      var loaded = /wf-(in)?active/.exec(document.documentElement.className);
      var success = loaded && !loaded[1]; // No "in" in the capture group.
      if (loaded) {
        success ? resolve() : reject();
      }
      else {
        var originalAciveCallback = WebFontConfig.active;
        WebFontConfig.inactive = reject;
        WebFontConfig.active = function() {
          originalAciveCallback();
          resolve();
        };
        // In case the webfont.js script failed to load.
        setTimeout(reject, WebFontConfig.timeout);
      }
    })
    .then(function() {
      var fontsActiveTime = measureDuration('fonts:active');
      if (fontsActiveTime) {
        ga('send', 'timing', 'Fonts', 'active', fontsActiveTime);
      }
    })
    .catch(function() {
      ga('send', 'event', 'Fonts', 'error');
    });
  }
}


/**
 * Calculates the time duration between the responseEnd timing event and when
 * all images are loaded and visible on the page, then sends this measurement
 * to Google Analytics via a timing hit.
 */
function measureImagesVisibleTime() {
  var imgVisibleTime = measureDuration('img:visible');
  if (imgVisibleTime) {
    ga('send', 'timing', 'Images', 'visible', imgVisibleTime);
  }
}


/**
 * Calculates the time duration between the responseEnd timing event and when
 * all synchronous JavaScript files are downloaded and executed, then sends
 * this measurement to Google Analytics via a timing hit.
 */
function measureJavaSciptExecutionTime() {
  var jsExecuteTime = measureDuration('js:execute');
  if (jsExecuteTime) {
    ga('send', 'timing', 'JavaScript', 'execute', jsExecuteTime);
  }
}


/**
 * Accepts a mark name and an optional reference point in the navigation timing
 * API and returns the time duration between the reference point and the last
 * mark (chronologically). The return value is rounded to the nearest whole
 * number to be compatible with Google Analytics.
 * @param {string} mark The mark name.
 * @param {string=} opt_reference An optional reference point from the
 *     navigation timing API. Defaults to 'responseEnd'.
 * @return {?number} The time duration as an integer or undefined if no
 *     matching marks can be found.
 */
function measureDuration(mark, opt_reference) {
  if (window.__perf) {
    var reference = opt_reference || 'responseEnd';
    var name = reference + ':' + mark;

    // Clears any existing measurements with the same name.
    performance.clearMeasures(name);

    // Creates a new measurement from the reference point to the specified mark.
    // If more than one mark with this name exists, the most recent one is used.
    performance.measure(name, reference, mark);

    // Gets the value of the measurement just created.
    var measure = performance.getEntriesByName(name)[0];

    // Returns the measure duration.
    return Math.round(measure.duration);
  }
}

El archivo index.html final debería verse de la siguiente manera:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Performance Analytics Demo</title>

  <!-- Start navigation timing feature detect -->
  <script>window.__perf = window.performance && performance.mark;</script>
  <!-- End navigation timing feature detect -->

  <!-- Start fonts -->
  <script>
  window.WebFontConfig = {
    google: {families: ['Roboto:400,700,400italic']},
    timeout: 10000,
    active: function() {
      __perf && performance.mark('fonts:active');
    }
  };
  </script>
  <script async src="https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js"></script>
  <!-- End fonts -->

  <!-- Start CSS -->
  <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
  <style>
    body { font-family: Roboto, sans-serif; margin: 1em; }
    img { float: left; height: auto; width: 33.33%; }
    .gallery { overflow: hidden; }
  </style>
  <script>__perf && performance.mark('css:unblock');</script>
  <!-- End CSS -->

</head>
<body>

  <div class="container">

    <!-- Start images -->
    <div class="gallery">
      <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/1/">
      <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/2/">
      <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/3/">
    </div>
    <script>__perf && performance.mark('img:visible')</script>
    <!-- End images -->

    <h1>Performance Analytics Demo</h1>
    <p>Real performance data from real users.</p>

  </div>

  <!-- Start JavaScript -->
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
  <script>__perf && performance.mark('js:execute');</script>
  <!-- End JavaScript -->

  <!-- Start analytics tracking snippet -->
  <script>
  window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
  ga('create', 'UA-XXXXX-Y', 'auto');
  ga('send', 'pageview');
  </script>
  <script async src="https://www.google-analytics.com/analytics.js"></script>
  <!-- End analytics tracking snippet -->

  <!-- Start performance analytics -->
  <script async src="perf-analytics.js"></script>
  <!-- End performance analytics -->

</body>
</html>

Si cargas esta página y observas las solicitudes en el panel de red, verás algo similar a lo siguiente:

Screen Shot 2016-05-10 at 6.57.23 PM.png

Esto es útil, pero puede ser engorroso ver estos datos como una solicitud codificada en URL. Y, si por algún motivo no ves estas solicitudes, es muy difícil rastrear dónde ocurrió la falla.

Un mejor enfoque cuando se desarrolla de forma local es usar la versión de depuración de analytics.js, que registrará información de depuración útil en la consola a medida que se ejecute cada comando de analytics.js. Si

actualiza la URL de analytics.js en index.html a analytics_debug.js y abre la consola del navegador. Verás instrucciones impresas como las siguientes:

Screen Shot 2016-05-10 at 6.54.13 PM.png

Ahora que sabes cómo implementar la medición del rendimiento para esta página de demostración, puedes intentar agregarla a tu propio sitio y enviar datos de usuarios reales a Google Analytics.

Genera informes sobre los datos que recopilaste

Después de recopilar datos de rendimiento durante unos días, podrás generar informes sobre esos datos para obtener estadísticas prácticas sobre la rapidez con la que se cargan tu sitio y sus recursos para los usuarios reales.

Para acceder a los informes de Tiempos de usuario en Google Analytics, haz clic en la pestaña Informes en la parte superior y selecciona "Comportamiento > Velocidad del sitio > Tiempos de usuario" en la navegación de la barra lateral (o sigue las instrucciones para ver el informe Tiempos de usuario en el Centro de ayuda).

Cuando cargues el informe de Tiempos de usuario en Google Analytics, deberías poder ver las categorías de tiempo que corresponden a los datos que enviaste. Haz clic en cualquiera de ellas para ver visualizaciones detalladas de tus datos de sincronización. La siguiente imagen es un ejemplo de los tiempos de carga de fuentes en un sitio web real que usa Google Fonts durante las últimas 24 horas.

Screen Shot 2016-05-10 at 7.16.07 PM.png

¡Felicitaciones! Completaste este lab de código correctamente. Si quieres profundizar más, en la siguiente sección, se te darán algunas sugerencias para que desarrolles este código y obtengas aún más estadísticas.

Las métricas de rendimiento que se abordan en este codelab son fundamentales para medir cómo se carga tu sitio para los usuarios reales, pero son solo el comienzo. Si quieres profundizar en el análisis del rendimiento, un próximo paso sencillo sería hacer un seguimiento de más métricas.

En este codelab, realizaste un seguimiento de las métricas relacionadas con el momento en que los recursos estaban disponibles para el usuario. Si lo deseas, puedes desglosar aún más la mayoría de estos elementos. Por ejemplo, en lugar de solo medir cuándo terminó de ejecutarse el código JavaScript, podrías medir cuándo comenzó a cargarse, cuándo terminó de cargarse, cuándo comenzó a ejecutarse y, finalmente, cuándo terminó de ejecutarse. Cada una de estas métricas podría revelar un problema que solo una de ellas no podría indicar.

Además de ser más detallado, también debes pensar de manera más integral en tu estrategia general de análisis del rendimiento. ¿Cuáles son los objetivos? ¿Qué es el éxito?

Cuando se trata de cualquier tipo de análisis, por lo general, querrás comenzar con alguna pregunta y, luego, descubrir cómo usar los datos para responderla.

Por ejemplo, considera la siguiente lista de preguntas y cómo usarías el conocimiento que adquiriste en este codelab para usar Analytics y responderlas:

  • ¿Los valores de las métricas que realizas un seguimiento disminuyen o aumentan con el tiempo?
  • ¿Cómo afectaría el uso del almacenamiento en caché sin conexión a través de un service worker o del almacenamiento local el rendimiento general de tu sitio?
  • ¿Tus recursos se cargan de manera óptima? Es decir, ¿hay una gran brecha entre el momento en que se descarga el recurso y el momento en que está disponible para su uso?
  • ¿Existe alguna correlación entre el rendimiento y otras métricas que supervisas (p. ej., la tasa de registros, el tiempo que se pasa en el sitio, las compras, etc.)?

Por último, si quieres obtener más información sobre el rendimiento web o Google Analytics, estos son algunos recursos excelentes para comenzar: