Uno de los beneficios más significativos de los servicios de trabajo (al menos desde una perspectiva de rendimiento) es su capacidad para controlar de forma proactiva el almacenamiento en caché de los recursos. Una aplicación web que pueda almacenar en caché todos sus recursos necesarios debería cargarse considerablemente más rápido para los visitantes recurrentes. Pero, ¿cómo se ven estas mejoras para los usuarios reales? ¿Y cómo se mide esto?
La app web de Google I/O (IOWA, en resumen) es una app web progresiva que aprovecha la mayoría de las nuevas funciones que ofrecen los trabajadores del servicio para ofrecer a sus usuarios una experiencia enriquecida similar a una app. También usó Google Analytics para recopilar datos de rendimiento y patrones de uso clave de su público de usuarios amplio y diverso.
En este caso de éxito, se explora cómo IOWA usó Google Analytics para responder preguntas clave de rendimiento y generar informes sobre el impacto real de los trabajadores del servicio.
Comienza con las preguntas
Cada vez que implementes estadísticas en un sitio web o una aplicación, es importante comenzar por identificar las preguntas que intentas responder a partir de los datos que recopilarás.
Si bien teníamos varias preguntas que queríamos responder, para los fines de este caso de éxito, centrémonos en dos de las más interesantes.
1. ¿La caché del trabajador del servicio tiene un mejor rendimiento que los mecanismos de almacenamiento en caché HTTP existentes disponibles en todos los navegadores?
Ya esperamos que las páginas se carguen más rápido para los visitantes recurrentes que para los nuevos, ya que los navegadores pueden almacenar en caché las solicitudes y entregarlas al instante en las visitas repetidas.
Los Service Workers ofrecen funciones de almacenamiento en caché alternativas que les brindan a los desarrolladores un control detallado sobre qué se almacena en caché y cómo se hace. En IOWA, optimizamos nuestra implementación de service worker para que cada recurso se almacenara en caché, de modo que los visitantes recurrentes pudieran usar la app completamente sin conexión.
Pero ¿esta tarea sería mejor que lo que el navegador ya hace de forma predeterminada? Si es así, ¿cuánto mejor? 1
2. ¿Cómo afecta el service worker la experiencia de carga del sitio?
En otras palabras, ¿qué tan rápido siente que se está cargando el sitio, independientemente de los tiempos de carga reales, según las métricas tradicionales de carga de la página?
Obviamente, no es una tarea fácil responder preguntas sobre cómo se siente una experiencia, y ninguna métrica representará a la perfección una opinión tan subjetiva. Dicho esto, sin duda hay algunas métricas que son mejores que otras, por lo que es importante elegir las correctas.
Elige la métrica correcta
De forma predeterminada, Google Analytics realiza un seguimiento de los tiempos de carga de la página (a través de la API de Navigation Timing) para el 1% de los visitantes de un sitio y pone esos datos a disposición a través de métricas como el Tiempo de carga promedio de la página.
El tiempo de carga promedio de la página es una buena métrica para responder nuestra primera pregunta, pero no es una métrica particularmente buena para responder la segunda. En primer lugar, el evento load
no corresponde necesariamente al momento en que el usuario puede interactuar con la app. Además, dos apps con el mismo tiempo de carga exacto pueden dar la sensación de que se cargan de manera muy diferente. Por ejemplo, un sitio con una pantalla de presentación o un indicador de carga probablemente parezca que se carga mucho más rápido que un sitio que solo muestra una página en blanco durante varios segundos.
En IOWA, mostramos una animación de cuenta regresiva de la pantalla de presentación que, en mi opinión, funcionó muy bien para entretener al usuario mientras el resto de la app se cargaba en segundo plano. Por lo tanto, hacer un seguimiento del tiempo que tarda en aparecer la pantalla de presentación tiene mucho más sentido como una forma de medir el rendimiento de carga percibido. Elegimos la métrica tiempo para la primera pintura para obtener este valor.
Una vez que decidimos qué preguntas queríamos responder e identificamos las métricas que serían útiles para responderlas, llegó el momento de implementar Google Analytics y empezar a medir.
La implementación de estadísticas
Si ya usaste Google Analytics, es probable que conozcas el fragmento de seguimiento de JavaScript recomendado. El aspecto resultante será el siguiente:
<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>
La primera línea del código anterior inicializa una función ga()
global (si aún no existe) y la última línea descarga la biblioteca analytics.js
de forma asíncrona.
La parte central contiene estas dos líneas:
ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');
Estos dos comandos realizan un seguimiento de las páginas que visitan las personas que llegan a tu sitio, pero no mucho más. Si quieres hacer un seguimiento de interacciones adicionales del usuario, debes hacerlo por tu cuenta.
En el caso de IOWA, queríamos hacer un seguimiento de dos aspectos adicionales:
- Es el tiempo transcurrido entre el momento en que la página comienza a cargarse y el momento en que aparecen los píxeles en la pantalla.
- Indica si un service worker controla la página o no. Con esta información, podríamos segmentar nuestros informes para comparar los resultados con y sin el trabajador de servicio.
Cómo capturar el tiempo hasta la primera pintura
Algunos navegadores registran la hora precisa en la que se pinta el primer píxel en la pantalla y ponen ese tiempo a disposición de los desarrolladores. Ese valor, en comparación con el valor de navigationStart
expuesto a través de la API de Navigation Timing nos brinda una contabilización muy precisa del tiempo que transcurrió entre el momento en que el usuario solicitó la página inicialmente y el momento en que vio algo por primera vez.
Como ya mencioné, el tiempo para el primer procesamiento de imagen es una métrica importante que se debe medir porque es el primer punto en el que un usuario experimenta la velocidad de carga de tu sitio. Es la primera impresión que tienen los usuarios, y una buena primera impresión puede afectar de forma positiva el resto de la experiencia del usuario.2
Para obtener el primer valor de pintura en los navegadores que lo exponen, creamos la función de utilidad getTimeToFirstPaintIfSupported
:
function getTimeToFirstPaintIfSupported() {
// Ignores browsers that don't support the Performance Timing API.
if (window.performance && window.performance.timing) {
var navTiming = window.performance.timing;
var navStart = navTiming.navigationStart;
var fpTime;
// If chrome, get first paint time from `chrome.loadTimes`.
if (window.chrome && window.chrome.loadTimes) {
fpTime = window.chrome.loadTimes().firstPaintTime * 1000;
}
// If IE/Edge, use the prefixed `msFirstPaint` property.
// See http://msdn.microsoft.com/ff974719
else if (navTiming.msFirstPaint) {
fpTime = navTiming.msFirstPaint;
}
if (fpTime && navStart) {
return fpTime - navStart;
}
}
}
Con esto, ahora podríamos escribir otra función que envíe un evento de no interacción con el tiempo de primera pintura como valor:3
function sendTimeToFirstPaint() {
var timeToFirstPaint = getTimeToFirstPaintIfSupported();
if (timeToFirstPaint) {
ga('send', 'event', {
eventCategory: 'Performance',
eventAction: 'firstpaint',
// Rounds to the nearest millisecond since
// event values in Google Analytics must be integers.
eventValue: Math.round(timeToFirstPaint)
// Sends this as a non-interaction event,
// so it doesn't affect bounce rate.
nonInteraction: true
});
}
}
Después de escribir ambas funciones, nuestro código de seguimiento se ve de la siguiente manera:
// Creates the tracker object.
ga('create', 'UA-XXXXX-Y', 'auto');
// Sends a pageview for the initial pageload.
ga('send', 'pageview');
// Sends an event with the time to first paint data.
sendTimeToFirstPaint();
Ten en cuenta que, según el momento en que se ejecute el código anterior, es posible que los píxeles ya se hayan pintado en la pantalla o no. Para asegurarnos de ejecutar este código siempre después de que se produce la primera pintura, pospusimos la llamada a sendTimeToFirstPaint()
hasta después del evento load
. De hecho, decidimos posponer el envío de todos los datos de estadísticas hasta después de que se cargue la página para garantizar que esas solicitudes no compitan con la carga de otros recursos.
// Creates the tracker object.
ga('create', 'UA-XXXXX-Y', 'auto');
// Postpones sending any hits until after the page has fully loaded.
// This prevents analytics requests from delaying the loading of the page.
window.addEventListener('load', function() {
// Sends a pageview for the initial pageload.
ga('send', 'pageview');
// Sends an event with the time to first paint data.
sendTimeToFirstPaint();
});
El código anterior informa firstpaint
veces a Google Analytics, pero esa es solo la mitad del proceso. Aún necesitábamos hacer un seguimiento del estado del service worker; de lo contrario, no podríamos comparar los primeros tiempos de pintura de una página controlada por un service worker y una página no controlada.
Cómo determinar el estado de un service worker
Para determinar el estado actual del trabajador de servicio, creamos una función de utilidad que muestra uno de los siguientes tres valores:
- controlada: Un service worker controla la página. En el caso de IOWA, eso también significa que todos los recursos se almacenaron en caché y que la página funciona sin conexión.
- compatible: El navegador admite el service worker, pero este aún no controla la página. Este es el estado esperado para los visitantes nuevos.
- No se admite: El navegador del usuario no admite el trabajador de servicio.
function getServiceWorkerStatus() {
if ('serviceWorker' in navigator) {
return navigator.serviceWorker.controller ? 'controlled' : 'supported';
} else {
return 'unsupported';
}
}
Esta función obtuvo el estado del trabajador del servicio por nosotros. El siguiente paso fue asociar este estado con los datos que enviamos a Google Analytics.
Realiza un seguimiento de los datos personalizados con dimensiones personalizadas
De forma predeterminada, Google Analytics te ofrece muchas formas de subdividir tu tráfico total en grupos según los atributos del usuario, la sesión o la interacción. Estos atributos se conocen como dimensiones. Las dimensiones comunes que les interesan a los desarrolladores web son, por ejemplo, el navegador, el sistema operativo o la categoría del dispositivo.
El estado del trabajador del servicio no es una dimensión que Google Analytics proporcione de forma predeterminada. Sin embargo, Google Analytics te permite crear tus propias dimensiones personalizadas y definirlas como desees.
Para IOWA, creamos una dimensión personalizada llamada Estado del trabajador de servicio y configuramos su alcance en hit (es decir, por interacción).4 Cada dimensión personalizada que creas en Google Analytics tiene un índice único dentro de esa propiedad y, en tu código de seguimiento, puedes hacer referencia a esa dimensión por su índice. Por ejemplo, si el índice de la dimensión que acabamos de crear fuera 1, podríamos actualizar nuestra lógica de la siguiente manera para enviar el evento firstpaint
y, de esta forma, incluir el estado del trabajador del servicio:
ga('send', 'event', {
eventCategory: 'Performance',
eventAction: 'firstpaint',
// Rounds to the nearest millisecond since
// event values in Google Analytics must be integers.
eventValue: Math.round(timeToFirstPaint)
// Sends this as a non-interaction event,
// so it doesn't affect bounce rate.
nonInteraction: true,
// Sets the current service worker status as the value of
// `dimension1` for this event.
dimension1: getServiceWorkerStatus()
});
Esto funciona, pero solo asociará el estado del trabajador de servicio con este evento en particular. Dado que el Estado del trabajador de servicio es algo que puede ser útil conocer para cualquier interacción, es mejor incluirlo con todos los datos que se envían a Google Analytics.
Para incluir esta información en todos los hits (p.ej., todas las vistas de página, los eventos, etc.), establecemos el valor de la dimensión personalizada en el objeto tracker antes de enviar datos a Google Analytics.
ga('set', 'dimension1', getServiceWorkerStatus());
Una vez establecido, este valor se envía con todos los hits posteriores de la carga de página actual. Si el usuario vuelve a cargar la página más adelante, es probable que se muestre un valor nuevo de la función getServiceWorkerStatus()
, y ese valor se establecerá en el objeto del servicio de seguimiento.
Nota breve sobre la claridad y la legibilidad del código: Dado que es posible que otras personas que vean este código no sepan a qué se refiere dimension1
, siempre es mejor crear una variable que asigne nombres de dimensiones significativos a los valores que usará analytics.js.
// Creates a map between custom dimension names and their index.
// This is particularly useful if you define lots of custom dimensions.
var customDimensions = {
SERVICE_WORKER_STATUS: 'dimension1'
};
// Creates the tracker object.
ga('create', 'UA-XXXXX-Y', 'auto');
// Sets the service worker status on the tracker,
// so its value is included in all future hits.
ga('set', customDimensions.SERVICE_WORKER_STATUS, getServiceWorkerStatus());
// Postpones sending any hits until after the page has fully loaded.
// This prevents analytics requests from delaying the loading of the page.
window.addEventListener('load', function() {
// Sends a pageview for the initial pageload.
ga('send', 'pageview');
// Sends an event with the time to first paint data.
sendTimeToFirstPaint();
});
Como mencioné, enviar la dimensión Estado del trabajador de servicio con cada hit nos permite usarla cuando se informa sobre cualquier métrica.
Como puedes ver, casi el 85% de todas las vistas de página de IOWA provinieron de navegadores que admiten service worker.
Los resultados: responder nuestras preguntas
Una vez que empezamos a recopilar datos para responder nuestras preguntas, pudimos generar informes sobre esos datos para ver los resultados. (Nota: Todos los datos de Google Analytics que se muestran aquí representan el tráfico web real al sitio de IOWA entre el 16 y el 22 de mayo de 2016).
La primera pregunta que hicimos fue la siguiente: ¿El almacenamiento en caché del service worker es más eficaz que los mecanismos existentes de almacenamiento en caché HTTP disponibles en todos los navegadores?
Para responder a esa pregunta, creamos un informe personalizado que analizaba la métrica Tiempos de carga promedio de las páginas en varias dimensiones. Esta métrica es adecuada para responder a esta pregunta, ya que el evento load
se activa solo después de que se descargan todos los recursos iniciales. Por lo tanto, refleja directamente el tiempo de carga total de todos los recursos esenciales del sitio.5
Las dimensiones que elegimos fueron las siguientes:
- Nuestra dimensión personalizada Estado de service worker
- Tipo de usuario, que indica si es la primera visita del usuario al sitio o si es un usuario recurrente. (Nota: Un visitante nuevo no tendrá ningún recurso almacenado en caché, pero un visitante recurrente sí).
- Categoría del dispositivo, que nos permite comparar los resultados entre dispositivos móviles y computadoras.
Para controlar la posibilidad de que factores no relacionados con el service worker estuvieran sesgando nuestros resultados de tiempo de carga, limitamos nuestra consulta para que solo incluya navegadores que admitan service worker.
Como puedes ver, las visitas a nuestra app cuando están controladas por un service worker se cargaron bastante más rápido que las visitas no controladas, incluso las de usuarios recurrentes que probablemente tenían la mayoría de los recursos de la página almacenados en caché. También es interesante observar que, en promedio, los visitantes que utilizan dispositivos móviles con un service worker registran cargas más rápidas que los nuevos visitantes de computadoras de escritorio.
"…las visitas a nuestra app cuando están controladas por un service worker se cargan bastante más rápido que las visitas no controladas…"
Puedes ver más detalles en las siguientes dos tablas:
Tiempo de carga promedio de la página (computadoras de escritorio) | |||
---|---|---|---|
Estado del service worker | Tipo de usuario | Tiempo promedio de carga de la página (ms) | Tamaño de la muestra |
Controlaste | Visitante recurrente | 2568 | 30860 |
Compatible | Visitante recurrente | 3612 | 1289 |
Compatible | Visitante nuevo | 4664 | 21991 |
Tiempo de carga promedio de la página (dispositivos móviles) | |||
---|---|---|---|
Estado del service worker | Tipo de usuario | Tiempo promedio de carga de la página (ms) | Tamaño de la muestra |
Controlaste | Visitante recurrente | 3760 | 8162 |
Compatible | Visitante recurrente | 4843 | 676 |
Compatible | Visitante nuevo | 6158 | 5779 |
Es posible que te preguntes cómo es posible que un visitante recurrente cuyo navegador admita el servicio de trabajador esté en un estado no controlado. Hay algunas posibles explicaciones para esto:
- El usuario abandonó la página en la visita inicial antes de que el service worker pudiera terminar de inicializarse.
- El usuario desinstaló el trabajador de servicio a través de las herramientas para desarrolladores.
Ambas situaciones son relativamente raras. Podemos ver eso en los datos si observamos los valores de Page Load Sample en la cuarta columna. Observa que las filas del medio tienen una muestra mucho más pequeña que las otras dos.
Nuestra segunda pregunta fue: ¿Cómo afecta el trabajador de servicio a la experiencia de carga del sitio?
Para responder esta pregunta, creamos otro informe personalizado para la métrica Promedio de valor del evento y filtramos los resultados para que solo incluyan nuestros eventos firstpaint
. Usamos las dimensiones Categoría de dispositivo y nuestra dimensión personalizada Estado del service worker.
Al contrario de lo que esperaba, el servicio de trabajo en dispositivos móviles tuvo mucho menos impacto en el tiempo de primera pintura que en la carga general de la página.
“…el trabajador de servicio en dispositivos móviles tuvo mucho menos impacto en el tiempo de primera pintura que en la carga general de la página”.
Para explorar a qué se debe este problema, debemos profundizar en los datos. Los promedios pueden ser buenos para obtener resúmenes generales y descripciones generales, pero para tener una idea real de cómo se desglosan estas cifras en un rango de usuarios, debemos observar una distribución de firstpaint
veces.
Cómo obtener la distribución de una métrica en Google Analytics
Para obtener la distribución de las veces de firstpaint
, necesitamos acceso a los resultados individuales de cada evento. Lamentablemente, Google Analytics no facilita esta tarea.
Google Analytics nos permite desglosar un informe según la dimensión que queramos, pero no usa el desglose por métricas. Eso no quiere decir que sea imposible, solo significa que tuvimos que personalizar un poco más nuestra implementación para obtener el resultado deseado.
Dado que los resultados de los informes solo se pueden desglosar por dimensiones, tuvimos que establecer el valor de la métrica (en este caso, la hora de firstpaint
) como una dimensión personalizada en el evento. Para ello, creamos otra dimensión personalizada llamada Valor de métrica y actualizamos nuestra lógica de seguimiento de firstpaint
de la siguiente manera:
var customDimensions = {
SERVICE_WORKER_STATUS: 'dimension1',
<strong>METRIC_VALUE: 'dimension2'</strong>
};
// ...
function sendTimeToFirstPaint() {
var timeToFirstPaint = getTimeToFirstPaintIfSupported();
if (timeToFirstPaint) {
var fields = {
eventCategory: 'Performance',
eventAction: 'firstpaint',
// Rounds to the nearest millisecond since
// event values in Google Analytics must be integers.
eventValue: Math.round(timeToFirstPaint)
// Sends this as a non-interaction event,
// so it doesn't affect bounce rate.
nonInteraction: true
}
<strong>// Sets the event value as a dimension to allow for breaking down the
// results by individual metric values at reporting time.
fields[customDimensions.METRIC_VALUE] = String(fields.eventValue);</strong>
ga('send', 'event', fields);
}
}
Actualmente, la interfaz web de Google Analytics no proporciona una forma de visualizar la distribución de valores de métricas arbitrarios, pero con la ayuda de la API de Google Analytics Core Reporting y la biblioteca de Google Charts, podemos consultar los resultados sin procesar y, luego, crear un histograma por nuestra cuenta.
Por ejemplo, se usó la siguiente configuración de solicitud de API para obtener una distribución de valores de firstpaint
en computadoras de escritorio con un trabajador de servicio no controlado.
{
dateRanges: [{startDate: '2016-05-16', endDate: '2016-05-22'}],
metrics: [{expression: 'ga:totalEvents'}],
dimensions: [{name: 'ga:dimension2'}],
dimensionFilterClauses: [
{
operator: 'AND',
filters: [
{
dimensionName: 'ga:eventAction',
operator: 'EXACT',
expressions: ['firstpaint']
},
{
dimensionName: 'ga:dimension1',
operator: 'EXACT',
expressions: ['supported']
},
{
dimensionName: 'ga:deviceCategory',
operator: 'EXACT',
expressions: ['desktop']
}
],
}
],
orderBys: [
{
fieldName: 'ga:dimension2',
orderType: 'DIMENSION_AS_INTEGER'
}
]
}
Esta solicitud a la API muestra un array de valores con el siguiente aspecto (Nota: Estos son solo los primeros cinco resultados). Los resultados se ordenan de menor a mayor, por lo que estas filas representan los tiempos más rápidos.
Resultados de la respuesta de la API (primeras cinco filas) | |
---|---|
ga:dimension2 | ga:totalEvents |
4 | 3 |
5 | 2 |
6 | 10 |
7 | 8 |
8 | 10 |
A continuación, te indicamos lo que significan estos resultados en lenguaje sencillo:
- Hubo 3 eventos en los que el valor de
firstpaint
fue de 4 ms - Hubo 2 eventos en los que el valor de
firstpaint
fue de 5 ms - Hubo 10 eventos en los que el valor de
firstpaint
fue de 6 ms. - Hubo 8 eventos en los que el valor de
firstpaint
fue de 7 ms. - Hubo 10 eventos en los que el
firstpaint
value
fue de 8 ms. - etc.
A partir de estos resultados, podemos extrapolar el valor firstpaint
para cada evento y crear un histograma de la distribución. Hicimos esto para cada una de las consultas que ejecutamos.
Así se veía la distribución en una computadora de escritorio con un service worker no controlado (pero compatible):
El tiempo firstpaint
medio para la distribución anterior es de 912 ms.
La forma de esta curva es bastante típica de las distribuciones de tiempo de carga. Compara esto con el histograma a continuación, que muestra la distribución de los eventos de primer procesamiento de imagen para las visitas en las que un service worker controla la página.
Ten en cuenta que, cuando un service worker controlaba la página, muchos visitantes experimentaron un primer procesamiento de imagen casi inmediato, con una mediana de 583 ms.
"... cuando un service worker controlaba la página, muchos visitantes experimentaron una primera pintura casi inmediata..."
Para tener una mejor idea de cómo se comparan estas dos distribuciones, en el siguiente gráfico, se muestra una vista combinada de ambas. El histograma que muestra las visitas de los trabajadores del servicio no controladas se superpone sobre el histograma que muestra las visitas controladas, y ambos se superponen sobre un histograma que muestra ambas combinadas.
Algo que me pareció interesante de estos resultados fue que la distribución con un service worker controlado aún tenía una curva en forma de campana después del aumento inicial. Esperaba un aumento repentino alto y, luego, un descenso gradual. No esperaba un segundo pico en la curva.
Cuando investigué qué podría estar causando esto, descubrí que, aunque un service worker puede controlar una página, su subproceso puede estar inactivo. El navegador hace esto para ahorrar recursos. Obviamente, no es necesario que todos los service workers de todos los sitios que visitaste estén activos y listos en todo momento. Esto explica la cola de la distribución. Algunos usuarios experimentaron una demora mientras se iniciaba el subproceso del service worker.
Sin embargo, como puedes ver en la distribución, incluso con este retraso inicial, los navegadores con el trabajador de servicio entregaron contenido más rápido que los navegadores que pasan por la red.
Así se veía en dispositivos móviles:
Si bien aún teníamos un aumento considerable en los tiempos de primera pintura casi inmediatos, la cola era bastante más grande y larga. Es probable que esto se deba a que, en dispositivos móviles, iniciar un subproceso de trabajador de servicio inactivo lleva más tiempo que en computadoras. También explica por qué la diferencia entre el tiempo promedio de firstpaint
no fue tan grande como esperaba (se analizó anteriormente).
“…en dispositivos móviles, iniciar un subproceso de service worker inactivo tarda más que en computadoras”.
A continuación, se muestra el desglose de estas variaciones de los tiempos medios de primera pintura en dispositivos móviles y computadoras de escritorio agrupados por estado del trabajador de servicio:
Tiempo medio hasta la primera pintura (ms) | ||
---|---|---|
Estado del service worker | Computadora de escritorio | Dispositivos móviles |
Controlaste | 583 | 1634 |
Compatible (no controlado) | 912 | 1933 |
Si bien la creación de estas visualizaciones de distribución requirió un poco más de tiempo y esfuerzo que la creación de un informe personalizado en Google Analytics, nos brindan una idea mucho mejor de cómo los trabajadores del servicio afectan el rendimiento de nuestro sitio que los promedios por sí solos.
Otro impacto de los service workers
Además del impacto en el rendimiento, los trabajadores del servicio también afectan la experiencia del usuario de otras maneras que se pueden medir con Google Analytics.
Acceso sin conexión
Los trabajadores del servicio permiten que los usuarios interactúen con tu sitio sin conexión. Si bien es probable que algún tipo de compatibilidad sin conexión sea fundamental para cualquier app web progresiva, determinar qué tan importante es en tu caso depende en gran medida de la cantidad de uso que se produce sin conexión. Pero ¿cómo lo medimos?
Para enviar datos a Google Analytics, se requiere una conexión a Internet, pero no es necesario que los datos se envíen en el momento exacto en que se produjo la interacción. Google Analytics admite el envío de datos de interacción después del hecho si especificas un desfase de tiempo (a través del parámetro qt
).
Durante los últimos dos años, IOWA ha utilizado una secuencia de comandos de service worker que detecta hits con errores de Google Analytics cuando el usuario no está conectado y los reproduce más tarde con el parámetro qt
.
Para hacer un seguimiento de si el usuario estaba en línea o sin conexión, creamos una dimensión personalizada llamada En línea y la configuramos con el valor de navigator.onLine
. Luego, escuchamos los eventos online
y offline
, y actualizamos la dimensión según corresponda.
Además, para tener una idea de lo habitual que era que un usuario se encontrara sin conexión mientras utilizaba IOWA, creamos un segmento orientado a los usuarios con, al menos, una interacción sin conexión. Resulta que fue casi el 5% de los usuarios.
Notificaciones push
Los service workers permiten que los usuarios acepten recibir notificaciones push. En IOWA, los usuarios recibían una notificación cuando estaba por comenzar una sesión de su programa.
Al igual que con cualquier forma de notificación, es importante encontrar el equilibrio entre proporcionar valor al usuario y molestarlo. Para comprender mejor lo que sucede, es importante hacer un seguimiento de si los usuarios aceptan recibir estas notificaciones, si interactúan con ellas cuando llegan y si los usuarios que anteriormente aceptaron cambiaron su preferencia y las inhabilitaron.
En IOWA, solo enviamos notificaciones relacionadas con la programación personalizada del usuario, algo que solo podían crear los usuarios que accedían a sus cuentas. Esto limitaba el conjunto de usuarios que podían recibir notificaciones para los usuarios que accedieron (se realiza un seguimiento mediante una dimensión personalizada denominada Acceso) cuyos navegadores admiten notificaciones push (se realiza un seguimiento mediante otra dimensión personalizada llamada Permiso de notificación).
El siguiente informe se basa en la métrica Usuarios y nuestra dimensión personalizada Permiso de notificaciones, segmentada por usuarios que accedieron en algún momento y cuyos navegadores admiten notificaciones push.
Es fantástico ver que más de la mitad de los usuarios que accedieron a sus cuentas optaron por recibir notificaciones push.
Banners de instalación de apps
Si una app web progresiva cumple con los criterios y un usuario la usa con frecuencia, es posible que se le muestre un banner de instalación de la app para que la agregue a su pantalla principal.
En IOWA, realizamos un seguimiento de la frecuencia con la que se mostraban estas instrucciones al usuario (y si se aceptaban) con el siguiente código:
window.addEventListener('beforeinstallprompt', function(event) {
// Tracks that the user saw a prompt.
ga('send', 'event', {
eventCategory: 'installprompt',
eventAction: 'fired'
});
event.userChoice.then(function(choiceResult) {
// Tracks the users choice.
ga('send', 'event', {
eventCategory: 'installprompt',
// `choiceResult.outcome` will be 'accepted' or 'dismissed'.
eventAction: choiceResult.outcome,
// `choiceResult.platform` will be 'web' or 'android' if the prompt was
// accepted, or '' if the prompt was dismissed.
eventLabel: choiceResult.platform
});
});
});
De los usuarios que vieron un banner de instalación de la app, alrededor del 10% eligió agregarlo a su pantalla principal.
Posibles mejoras en el seguimiento (para la próxima vez)
Los datos estadísticos que recopilamos de IOWA este año fueron invaluables. Pero la retrospectiva siempre arroja luz y oportunidades para mejorar la próxima vez. Después de finalizar el análisis de este año, te contamos dos cosas que me gustaría haber hecho de manera diferente y que los lectores que quieran implementar una estrategia similar podrían considerar:
1. Hacer un seguimiento de más eventos relacionados con la experiencia de carga
Realizamos un seguimiento de varios eventos que corresponden a una métrica técnica (p.ej., HTMLImportsLoaded, WebComponentsReady, etc.), pero como gran parte de la carga se realizó de forma asíncrona, el punto en el que se activaron estos eventos no necesariamente correspondía a un momento determinado de la experiencia de carga general.
El evento principal relacionado con la carga del que no realizamos un seguimiento (pero nos gustaría haberlo hecho) es el punto en el que desapareció la pantalla de presentación y el usuario pudo ver el contenido de la página.
2. Almacena el ID de cliente de Analytics en IndexedDB
De forma predeterminada, analytics.js almacena el campo de ID de cliente en las cookies del navegador. Lamentablemente, las secuencias de comandos del service worker no pueden acceder a las cookies.
Esto nos presentó un problema cuando intentamos implementar el seguimiento de notificaciones. Queríamos enviar un evento desde el trabajador de servicio (a través del Protocolo de medición) cada vez que se enviaba una notificación a un usuario y, luego, hacer un seguimiento del éxito de la reactivación de la participación de esa notificación si el usuario hacía clic en ella y regresaba a la app.
Si bien pudimos hacer un seguimiento del éxito de las notificaciones en general a través del parámetro de campaña utm_source
, no pudimos vincular una sesión de reactivación de la participación a un usuario en particular.
Para evitar esta limitación, podríamos haber almacenado el ID de cliente a través de IndexedDB en nuestro código de seguimiento y, luego, la secuencia de comandos del trabajador del servicio podría haber accedido a ese valor.
3. Permite que el service worker informe el estado en línea o sin conexión
Si inspeccionas navigator.onLine
, sabrás si tu navegador puede conectarse al router o a la red de área local, pero no te indicará si el usuario tiene conectividad real. Y debido a que nuestra secuencia de comandos del service worker de análisis sin conexión simplemente volvió a reproducir hits fallidos (sin modificarlos ni marcarlos como fallidos), es probable que no tengamos informes de nuestro uso sin conexión.
En el futuro, debemos hacer un seguimiento del estado de navigator.onLine
y de si el service worker volvió a reproducir el hit debido a una falla inicial de red. Esto nos dará una imagen más precisa del uso real sin conexión.
Conclusión
Este caso de éxito demostró que el uso del trabajador de servicio mejoró el rendimiento de carga de la aplicación web de Google I/O en una amplia variedad de navegadores, redes y dispositivos. También se demostró que, cuando se observa una distribución de los datos de carga en una amplia variedad de navegadores, redes y dispositivos, se obtienen muchas más estadísticas sobre cómo esta tecnología maneja situaciones reales y se descubren características de rendimiento que quizás no se esperaban.
Estas son algunas de las conclusiones clave del estudio de IOWA:
- En promedio, las páginas se cargaron bastante más rápido cuando un service worker controlaba la página que sin él, tanto para los visitantes nuevos como para los recurrentes.
- Las visitas a las páginas controladas por un service worker se cargaron casi al instante para muchos usuarios.
- Cuando estaban inactivos, los trabajadores de servicio tardaban un poco en iniciarse. Sin embargo, un service worker inactivo aún tenía un mejor rendimiento que no tener uno.
- El tiempo de inicio de un service worker inactivo fue mayor en los dispositivos móviles que en las computadoras de escritorio.
Si bien las mejoras en el rendimiento observadas en una aplicación en particular son generalmente útiles para informar a la comunidad más amplia de desarrolladores, es importante recordar que estos resultados son específicos para el tipo de sitio de IOWA (un sitio de eventos) y el tipo de público que tiene (en su mayoría, los desarrolladores).
Si implementas un trabajador de servicio en tu aplicación, es importante que implementes tu propia estrategia de medición para que puedas evaluar tu propio rendimiento y evitar regresiones futuras. Si es así, comparte tus resultados para que todos puedan beneficiarse.
Pies de página
- No es del todo justo comparar el rendimiento de la implementación de la caché de nuestro service worker con el rendimiento de nuestro sitio solo con la caché HTTP. Como estábamos optimizando IOWA para el trabajador de servicio, no dedicamos mucho tiempo a optimizarlo para la caché HTTP. Si lo hubiéramos hecho, los resultados probablemente habrían sido diferentes. Si quieres obtener más información sobre cómo optimizar tu sitio para el almacenamiento en caché HTTP, consulta Cómo optimizar el contenido de manera eficiente.
- Según la forma en que tu sitio cargue sus estilos y contenido, es posible que el navegador pueda pintar antes de que el contenido o los estilos estén disponibles. En esos casos,
firstpaint
puede corresponder a una pantalla en blanco. Si usasfirstpaint
, es importante que te asegures de que corresponda a un punto significativo en la carga de los recursos de tu sitio. - Técnicamente, podríamos enviar un hit de timing (que no son interacción de forma predeterminada) para capturar esta información en lugar de un evento. De hecho, los hits de tiempo se agregaron a Google Analytics específicamente para hacer un seguimiento de las métricas de carga como esta. Sin embargo, los hits de tiempo se muestrean mucho durante el tiempo de procesamiento y sus valores no se pueden usar en los segmentos. Dadas estas limitaciones actuales, los eventos sin interacción siguen siendo más adecuados.
- Para comprender mejor qué alcance debes asignar a una dimensión personalizada en Google Analytics, consulta la sección Dimensión personalizada del Centro de ayuda de Analytics. También es importante comprender el modelo de datos de Google Analytics, que consta de usuarios, sesiones e interacciones (hits). Para obtener más información, mira la lección de Analytics Academy sobre el modelo de datos de Google Analytics.
- Esto no considera los recursos que se cargan de forma diferida después del evento de carga.