Medición de 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 a fin de mejorar la experiencia de tus usuarios.

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

Si tienes un sitio web, es probable que ya estés usando Google Analytics para medir el tráfico y el uso de navegadores y dispositivos. Con solo un poco de código adicional, puede agregar métricas de rendimiento a la combinación.

Qué aprenderás

  • Cómo medir las métricas de rendimiento con precisión y exactitud mediante la API de User Timings
  • Cómo enviar esos datos a Google Analytics para que se puedan incorporar en sus informes

Requisitos

¿Cómo usarás este instructivo?

Ler Leer y completar los ejercicios

¿Cómo calificarías tu experiencia cuando compilas sitios o aplicaciones web?

Principiante Intermedio Avanzado

Puedes 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 en este codelab. Puedes usar esta opción para omitir con facilidad el codelab o verificar que la implementación sea correcta.

Si tienes acceso a un programa de diffing, puedes usarlo para ver exactamente los cambios de un paso a otro.

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

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

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

Consideraciones de rendimiento de los elementos

Si alguna vez leyó algo sobre la optimización del rendimiento, es probable que ya sepa que cada uno de estos tipos de elementos tienen sus propias particularidades y puede afectar el rendimiento percibido en general de varias maneras.

CSS

Por ejemplo, las hojas de estilo bloquean el procesamiento de todos los elementos del DOM que vienen después, lo cual significa que el navegador tiene que hacer una solicitud para la hoja de estilo, descargarla y analizarla antes de procesar cualquier contenido del DOM que aparezca después. Por este motivo, generalmente es mejor colocar las hojas de estilo en el <head> del documento. Debido a la naturaleza de bloqueo de los CSS, también se recomienda colocarlos solo en el encabezado y cargar los CSS de forma asíncrona más adelante.

JavaScript

Por otro lado, JavaScript no bloquea la renderización, pero sí bloquea el análisis y la construcción del DOM. Esto es necesario porque JavaScript puede modificar el DOM. Esto significa que cada vez que el navegador detecta una etiqueta <script> (sin incluir 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 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 bloquear la renderización del documento hasta que la fuente esté disponible para usarla. En este caso, es fundamental comprender cuánto les lleva a los usuarios realmente. Es posible que la fuente web se cargue rápidamente, pero muy lentamente para la mayoría de las personas que visitan su sitio. Por eso es tan importante tomar decisiones y medir los datos reales.

Imágenes

Por último, las imágenes pueden lograr que un sitio cobre vida, pero también suelen tardar más en cargarse. Comprender lo que esto significa realmente y poder detectar las correlaciones entre los patrones de uso y los tiempos de carga de las páginas es fundamental para comprender cómo optimizar.

El primer paso de este codelab es ver cómo luce la página de demostración antes de agregar código de medición de rendimiento.

Para ver la demostración, cree una carpeta nueva y agréguele un archivo llamado index.html. Luego, copie y pegue 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>

Luego, abre el servidor web para Chrome e inicia un servidor local en el directorio que acabas de crear. Asegúrate de que la función "muestra automáticamente index.html"

Captura de pantalla del 2016-05-11 a la 1.03.43 PM.png

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

Captura de pantalla del 2016-05-11 a las 10.59.03 AM.png

Una vez que tengas la página de demostración en ejecución, tómate un momento para mirar el código y ver todos los diferentes tipos de activos que se están cargando. En los próximos pasos, agregarás código para medir cuándo se cargan los elementos y el usuario puede interactuar con ellos.

Como se mencionó en la sección de consideraciones de rendimiento de los elementos, CSS bloquea la renderización de los elementos DOM y la ejecución de secuencias de comandos que vienen después.

El archivo de demostración que acaba de crear contiene el siguiente CSS, que hace referencia a Bootstrap y 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 el CSS bloquea la renderización de los elementos DOM y la ejecución de las secuencias de comandos, es posible determinar cuándo se termina el bloqueo del CSS agregando una etiqueta <script> inmediatamente después del CSS que almacena la hora actual.

Para hacerlo, puedes crear una variable y asignarle new Date(), pero, gracias a la API de User Timings, el método performance.mark es mucho más fácil.

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 <!-- End CSS --> de cierre.

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

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

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

La siguiente función de utilidad mide y muestra el período de tiempo entre una marca que agregaste y la marca responseEnd que creó 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, luego, copia y pega el código anterior en él.

Ahora que se definió esta función, puedes llamarla y pasar el nombre de la marca "css:unblock" Para no interferir con ninguna otra carga de recursos, debes diferir la ejecución de estas mediciones hasta 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 agregarla últimamente para que no interfiera en la carga de otros recursos.

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

Una vez que hayas completado este paso, el código debe coincidir con lo que se encuentra en el directorio 01-css del repositorio de codelab.

Si cargas la página en un navegador y abres Developer Console, deberías ver algo como el siguiente resultado:

Captura de pantalla del 2016-05-17 a las 11.13.02 AM.png

Las fuentes web normalmente se cargan a través de una hoja de estilo externa, como se ve 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 -->

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

Desafortunadamente, no es tan sencillo.

Las hojas de estilo bloquean la ejecución de JavaScript porque su contenido se usa para construir el CSSOM y, como es posible que el JavaScript que se carga necesite acceso al CSSOM, esta se retrase hasta que se construya el CSSOM por completo.

La falta es que el navegador puede construir el CSSOM sin descargar la fuente. Esto significa que si agrega una marca a través de una etiqueta de secuencia de comandos integrada al DOM inmediatamente después de la etiqueta de hoja de estilo <link> font, es probable que la marca ocurra 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 usar en la página. Afortunadamente, cargar fuentes mediante JavaScript también implica una mejora en 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 fue desarrollada en conjunto por Google y Typekit.

Si desea actualizar el documento principal para usar webfont.js a fin de cargar las fuentes (en lugar de la etiqueta <link>), reemplace 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:

  • Se crea una marca "fonts:active" en la devolución de llamada activa, para que luego puedas medir el tiempo que tardaron en cargarse las fuentes.
  • La etiqueta <script> que carga webfonts.js contiene el atributo async, por lo que no se bloqueará el análisis ni la renderización del resto del documento (lo que no se aplica para las etiquetas <link>).

Si bien los códigos anteriores crean una marca "fonts:active" medirla y registrarla en la consola no es tan simple como lo fue para la marca "css:unblock" El motivo es que la carga de la fuente ahora se realiza de forma asíncrona, por lo que, si intentas medir la marca " en el controlador window.onload (como lo hiciste con "css:unblock"), es muy probable que la fuente 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 automáticamente. Cópielo y péguelo 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 llamar a esta nueva función.

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

Una vez que hayas completado este paso, el código debe coincidir con lo que se encuentra en el directorio 02-fonts del repositorio de codelab.

Si cargas la página en un navegador y abres Developer Console, deberías ver algo como el siguiente resultado:

Captura de pantalla del 2016-05-17 a las 11.13.22 AM.png

Saber cuándo una imagen es visible no es tan simple como podría parecer. A partir de los pasos anteriores, sabes que las hojas de estilo y las etiquetas <script> síncronas pueden bloquear la renderización, el análisis y la ejecución de secuencias de comandos. Es posible que no sepas que ninguna de ellas bloquea el escáner precargado del navegador.

Un escáner precargado es algo que todos los navegadores modernos implementan como uno de los muchos intentos para mejorar el rendimiento, incluso en sitios sin rendimiento que contienen una gran cantidad de elementos de bloqueo. La idea es que mientras algunos elementos pueden bloquear el análisis o la renderización o la ejecución de la secuencia de comandos, no tienen que bloquear las descargas. Por lo tanto, el navegador escanea el archivo HTML antes de comenzar a construir el DOM y busca los elementos que puede comenzar a descargar de inmediato.

En lo que respecta a las imágenes, esto significa que es probable que tus imágenes ya se descarguen en el momento en que se agreguen al DOM. Esto también significa que el punto en el que se descarga una imagen no necesariamente es una buena métrica de rendimiento. La métrica de rendimiento que debe considerar es el punto en el que una imagen es visible para el usuario.

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

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

Para ello, agregue marcas en cada controlador de carga de imágenes y en la etiqueta intercalada <gt; inmediatamente después de la última imagen del DOM. La idea es que la última marca sea la que representa que todas las imágenes serán visibles.

Para agregar marcas cuando se carguen las imágenes y cuando se rendericen, 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 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 llamarlo:

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

Una vez que hayas completado este paso, el código debe coincidir con lo que se encuentra en el directorio 03-images del repositorio de codelab.

Si cargas la página en un navegador y abres Developer Console, deberías ver algo como el siguiente resultado:

Captura de pantalla del 2016-05-17 a las 11.13.39 AM.png

Dado que las etiquetas <script> sin el atributo async bloquean el análisis del DOM hasta que se hayan descargado y ejecutado, 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, el uso de los controladores onload no funcionará, ya que el navegador debe ejecutar la secuencia de comandos después de cargarla y eso lleva tiempo. Una secuencia de comandos que se carga rápido, pero se puede ejecutar con lentitud, puede ser tan mala como una secuencia de comandos de carga lenta.

Para realizar un seguimiento de cuando JavaScript se carga y ejecuta en el documento principal, actualiza la sección 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 las secuencias de comandos de los complementos de jQuery y Bootstrap se hayan descargado y terminado de ejecutarse.

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'));
}

Y, luego, invocarlo desde el controlador window.onload:

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

Una vez que hayas completado este paso, el código debe coincidir con lo que se encuentra en el directorio 04-javascript del repositorio de codelab.

Si cargas la página en un navegador y abres Developer Console, deberías ver algo como el siguiente resultado:

Captura de pantalla del 2016-05-17 a las 11.14.03 AM.png

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

Para ello, 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 parte de index.html en la que llames a performance.mark, agrégale el prefijo con la detección de características. Este es un ejemplo que actualiza el código en el bloque de imágenes, pero también debes asegurarte de actualizar las otras secciones.

<!-- 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.

La función measureDuration debe actualizarse para agregar la siguiente condición:

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

Por último, como no todos los navegadores admiten las promesas de JavaScript, la función measureWebfontPerfAndFailures también debe unirse en un condicional:

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

Ahora debería poder ejecutar su código en cualquier navegador sin problemas.

Una vez que hayas completado este paso, el código debe coincidir con lo que se encuentra en el directorio 05-feature-detects del repositorio de codelab.

El último paso de este codelab es tomar los datos que se registran en la consola y, en su lugar, enviarlos a Google Analytics. Para poder enviar datos a Google Analytics, primero debe agregar la biblioteca analytics.js y el fragmento de seguimiento predeterminado a su página.

Agrega el siguiente código a index.html después del bloque JavaScript principal, 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 anteriormente, sabrás que debes reemplazar el marcador de posición UA-XXXXX-Y&quot por el ID de seguimiento que recibiste cuando creaste la propiedad nueva en Google Analytics.

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

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

Si bien los datos recopilados solo de las páginas vistas son útiles, no cuentan la historia completa. Para comprender mejor cómo los usuarios experimentan su sitio o aplicación, debe enviar los datos de interacción adicionales a Google Analytics.

Google Analytics admite varios tipos de datos de interacción: páginas vistas, eventos, interacciones sociales, excepciones y, por último, tiempos de usuario. Para enviar datos de tiempos de los usuarios a Google Analytics, puedes usar la siguiente firma de comando:

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

Donde timingCategory es una string que te permite organizar los hits de tiempo en un grupo lógico, timingVar es la variable que estás midiendo, y timingValue es la duración real en milisegundos.

Para ver cómo funciona en la práctica, la sentencia 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 se ejecute solo si el navegador admite la API de User Timings, lo que significa que, a veces, mostrará undefined. Dado que no hay motivo para enviar datos no definidos a Google Analytics (en algunos casos, incluso podrían confundir sus informes), solo debe enviar este hit de tiempo si measureDuration muestra un valor.
  • Cuando measureDuration muestra un valor, es un DOMHighResTimeStamp, que tendrá una precisión superior a milisegundos. Como timingValue en Google Analytics debe ser un número entero, debes redondear el valor que muestra measureDuration.

Para tener en cuenta estos problemas, actualice la sentencia de retorno 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, actualice los comandos de sincronización para que se ejecuten solo si existe un valor para la métrica en cuestión. A modo de ejemplo, la función measureCssUnblockTime debe actualizarse de la siguiente manera:

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

Deberás realizar actualizaciones similares a las demás funciones de medición. Una vez que se complete, el archivo final perf-analytics.js debería verse así:

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 así:

<!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 ves las solicitudes en el panel de red, verás algo como lo siguiente:

Captura de pantalla del 2016-05-10 a las 6.57.23 PM.png

Esto es útil, pero puede resultar complicado considerar estos datos como una solicitud codificada en formato URL. Además, si por alguna razón no ves estas solicitudes, es muy difícil rastrear dónde se produjo el error.

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

actualice la URL analytics.js en index.html a analytics_debug.js y abra la consola del navegador; verá sentencias impresas como las siguientes:

Captura de pantalla del 2016-05-10 a las 6.54.13 PM.png

Ahora que sabe cómo implementar la medición del rendimiento en esta página de demostración, puede agregarla a su propio sitio y enviar datos reales del usuario a Google Analytics.

Informes sobre los datos que recopila

Una vez que hayas recopilado datos de rendimiento durante algunos 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 usuarios reales.

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

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

Captura de pantalla del 2016-05-10 a las 7.16.07 PM.png

¡Felicitaciones! Completaste correctamente este codelab. En caso de que quieras profundizar en el tema, en la siguiente sección encontrarás algunas sugerencias de cómo compilar sobre este código para obtener 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 deseas ahondar en las estadísticas de rendimiento, una simple tarea sería simplemente hacer un seguimiento de más métricas.

En este codelab, hiciste un seguimiento de las métricas relacionadas con la disponibilidad de recursos para el usuario. Si quisieras, puedes desglosar la mayoría de ellos aún más. Por ejemplo, en lugar de solo medir cuándo termina de ejecutarse JavaScript, puedes medir cuándo comenzó a cargarse, cuándo terminó de ejecutarse, cuándo comenzó a ejecutarse y, por último, cuándo terminó de ejecutarse. Cada una de estas métricas podría revelar un problema que solo una de ellas podría no revelar.

Además de ser más detallados, también debe pensar de manera más integral en su estrategia general de estadísticas de rendimiento. ¿Cuáles son los objetivos? ¿Qué es el éxito?

Cuando se trata de algún tipo de analítica, por lo general, querrás comenzar con un tipo de pregunta y, luego, averiguar cómo usar los datos para responderla.

Por ejemplo, considera la siguiente lista de preguntas, y cómo utilizar los conocimientos que adquiriste en este codelab para utilizar Analytics a fin de responderlas:

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

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