Como medir o impacto do desempenho no mundo real dos service workers

Um dos benefícios mais significativos dos service workers (pelo menos do ponto de vista de desempenho) é a capacidade de controlar proativamente o armazenamento em cache de recursos. Um aplicativo da Web que pode armazenar em cache todos os recursos necessários precisa carregar muito mais rápido para visitantes recorrentes. Mas como esses ganhos são percebidos pelos usuários reais? E como medir isso?

O app da Web do Google I/O (IOWA, na sigla em inglês) é um app da Web progressivo que aproveitou a maioria dos novos recursos oferecidos pelos service workers para oferecer uma experiência rica e semelhante a um app aos usuários. A empresa também usou o Google Analytics para capturar os principais dados de performance e padrões de uso do grande e diversificado público-alvo de usuários.

Este estudo de caso mostra como a IOWA usou o Google Analytics para responder a perguntas importantes sobre desempenho e informar sobre o impacto real dos service workers.

Começar com as perguntas

Sempre que você implementar a análise em um site ou aplicativo, é importante começar identificando as perguntas que você quer responder com os dados que vai coletar.

Embora tivéssemos várias perguntas que queríamos responder, para fins deste estudo de caso, vamos nos concentrar em duas das mais interessantes.

1. O armazenamento em cache do service worker tem melhor desempenho do que os mecanismos de armazenamento em cache HTTP disponíveis em todos os navegadores?

Já esperamos que as páginas carreguem mais rápido para visitantes recorrentes do que para novos visitantes, já que os navegadores podem armazenar solicitações em cache e exibi-las instantaneamente em visitas repetidas.

Os service workers oferecem recursos de armazenamento em cache alternativos que dão aos desenvolvedores um controle refinado sobre exatamente o que e como o armazenamento em cache é feito. No IOWA, otimizamos a implementação do service worker para que todos os recursos fossem armazenados em cache, para que os visitantes recorrentes pudessem usar o app completamente off-line.

Mas esse esforço seria melhor do que o que o navegador já faz por padrão? Se sim, quanto melhor? 1

2. Como o service worker afeta a experiência de carregamento do site?

Em outras palavras, quão rápido parece que o site está carregando, independentemente dos tempos de carregamento reais medidos pelas métricas tradicionais de carregamento de página?

Obviamente, responder a perguntas sobre a sensação de uma experiência não é uma tarefa fácil, e nenhuma métrica vai representar perfeitamente esse sentimento subjetivo. Dito isso, algumas métricas são melhores do que outras, então é importante escolher as certas.

Como escolher a métrica certa

Por padrão, o Google Analytics rastreia os tempos de carregamento da página (pela API Navigation Timing) de 1% dos visitantes de um site e disponibiliza esses dados por meio de métricas como "Tempo médio de carregamento da página".

Tempo médio de carregamento da página é uma boa métrica para responder à primeira pergunta, mas não é uma métrica particularmente boa para responder à segunda. Por exemplo, o evento load não corresponde necessariamente ao momento em que o usuário pode interagir com o app. Além disso, dois apps com o mesmo tempo de carregamento podem parecer carregar de maneiras muito diferentes. Por exemplo, um site com uma tela de apresentação ou um indicador de carregamento provavelmente parece carregar muito mais rápido do que um site que mostra uma página em branco por vários segundos.

No IOWA, mostramos uma animação de contagem regressiva na tela de apresentação que, na minha opinião, serviu para entreter o usuário enquanto o restante do app era carregado em segundo plano. Por isso, acompanhar o tempo que a tela de apresentação leva para aparecer é uma maneira muito mais sensata de medir a percepção de desempenho de carregamento. Escolhemos a métrica tempo até a primeira pintura para conseguir esse valor.

Depois de decidirmos as perguntas que queríamos responder e identificar as métricas que seriam úteis para isso, chegou a hora de implementar o Google Analytics e começar a medir.

Implementação do Google Analytics

Se você já usou o Google Analytics, provavelmente já conhece o snippet de acompanhamento recomendado do JavaScript. Esta é a aparência dela:

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

A primeira linha do código acima inicializa uma função ga() global (se ela ainda não existir), e a última linha faz o download assíncrono da biblioteca analytics.js.

A parte do meio contém estas duas linhas:

ga('create', 'UA-XXXXX-Y', 'auto');
ga
('send', 'pageview');

Esses dois comandos rastreiam quais páginas são visitadas pelas pessoas que acessam seu site, mas não muito mais do que isso. Se você quiser acompanhar outras interações do usuário, faça isso por conta própria.

Para a IOWA, queríamos acompanhar duas outras coisas:

  • O tempo decorrido entre o início do carregamento da página e a exibição dos pixels na tela.
  • Se um service worker está controlando a página ou não. Com essas informações, podemos segmentar nossos relatórios para comparar os resultados com e sem o service worker.

Como capturar o tempo até a primeira pintura

Alguns navegadores registram o momento exato em que o primeiro pixel é pintado na tela e disponibilizam esse momento para os desenvolvedores. Esse valor, comparado ao valor navigationStart exposto pela API Navigation Timing, fornece uma estimativa muito precisa de quanto tempo passou entre o momento em que o usuário solicitou a página pela primeira vez e o momento em que ele viu algo.

Como já mencionei, o tempo para a first paint é uma métrica importante porque é o primeiro ponto em que o usuário experimenta a velocidade de carregamento do site. É a primeira impressão que os usuários têm, e uma boa primeira impressão pode afetar positivamente o restante da experiência do usuário.2

Para receber o primeiro valor de pintura nos navegadores que o expõem, criamos a função de utilitário 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;
   
}
 
}
}

Com isso, agora podemos escrever outra função que envia um evento de não interação com o tempo para a primeira 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
   
});
 
}
}

Depois de escrever essas duas funções, nosso código de rastreamento fica assim:

// 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
();

Dependendo de quando o código acima é executado, os pixels podem ou não ter sido pintados na tela. Para garantir que esse código seja executado sempre após a primeira pintura, adiamos a chamada para sendTimeToFirstPaint() até depois do evento load. Na verdade, decidimos adiar o envio de todos os dados de análise até que a página fosse carregada para garantir que essas solicitações não competissem com o carregamento de outros 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
();
});

O código acima informa firstpaint vezes ao Google Analytics, mas isso é apenas metade da história. Ainda precisávamos acompanhar o status do service worker. Caso contrário, não seria possível comparar os tempos de primeira pintura de uma página controlada por um service worker e uma página não controlada.

Como determinar o status do service worker

Para determinar o status atual do worker de serviço, criamos uma função utilitária que retorna um destes três valores:

  • Controlada: um service worker está controlando a página. No caso do IOWA, isso também significa que todos os recursos foram armazenados em cache e a página funciona off-line.
  • Suportado: o navegador oferece suporte a service worker, mas ele ainda não controla a página. Esse é o status esperado para visitantes novos.
  • Sem suporte: o navegador do usuário não oferece suporte a service worker.
function getServiceWorkerStatus() {
 
if ('serviceWorker' in navigator) {
   
return navigator.serviceWorker.controller ? 'controlled' : 'supported';
 
} else {
   
return 'unsupported';
 
}
}

Essa função recebeu o status do worker do serviço. A próxima etapa foi associar esse status aos dados que estávamos enviando ao Google Analytics.

Como acompanhar dados personalizados com dimensões personalizadas

Por padrão, o Google Analytics oferece várias maneiras de dividir seu tráfego total em grupos com base nos atributos do usuário, da sessão ou da interação. Esses atributos são conhecidos como dimensões. As dimensões comuns que os desenvolvedores da Web consideram são Navegador, Sistema operacional ou Categoria de dispositivo.

O status do worker do serviço não é uma dimensão fornecida pelo Google Analytics por padrão. No entanto, o Google Analytics permite que você crie suas próprias dimensões personalizadas e as defina como quiser.

Para o IOWA, criamos uma dimensão personalizada chamada Status do service worker e definimos o escopo como hit (ou seja, por interação).4 Cada dimensão personalizada criada no Google Analytics recebe um índice exclusivo nessa propriedade, e no código de acompanhamento é possível fazer referência a essa dimensão pelo índice. Por exemplo, se o índice da dimensão que acabamos de criar fosse 1, poderíamos atualizar nossa lógica da seguinte maneira para enviar o evento firstpaint e incluir o status do service worker:

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

Isso funciona, mas só associa o status do service worker a esse evento específico. Como o status do service worker é algo que pode ser útil para qualquer interação, é melhor incluí-lo com todos os dados enviados ao Google Analytics.

Para incluir essas informações em todos os hits (por exemplo, todas as visualizações de página, eventos etc.), definimos o valor da dimensão personalizada no próprio objeto rastreador antes de enviar qualquer dado ao Google Analytics.

ga('set', 'dimension1', getServiceWorkerStatus());

Depois de definido, esse valor é enviado com todos os hits subsequentes para o carregamento de página atual. Se o usuário carregar a página novamente mais tarde, um novo valor provavelmente será retornado pela função getServiceWorkerStatus() e esse valor será definido no objeto do rastreador.

Observação rápida sobre a clareza e a legibilidade do código: como outras pessoas que analisam esse código podem não saber a que dimension1 se refere, é sempre melhor criar uma variável que mapeia nomes de dimensões significativos para os valores que a analytics.js vai usar.

// 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 mencionei, o envio da dimensão Status do service worker com cada hit nos permite usá-la ao gerar relatórios sobre qualquer métrica.

Como você pode ver, quase 85% de todas as visualizações de página da IOWA foram de navegadores que oferecem suporte a service worker.

Os resultados: respostas às nossas perguntas

Depois que começamos a coletar dados para responder às nossas perguntas, foi possível gerar relatórios com esses dados para conferir os resultados. Observação: todos os dados do Google Analytics mostrados aqui representam o tráfego da Web real para o site da IOWA de 16 a 22 de maio de 2016.

A primeira pergunta que fizemos foi: O cache de service workers tem mais performance do que os mecanismos de cache HTTP disponíveis em todos os navegadores?

Para responder a essa pergunta, criamos um relatório personalizado que analisou a métrica Tempo médio de carregamento da página em várias dimensões. Essa métrica é adequada para responder a essa pergunta, porque o evento load é acionado somente depois que todos os recursos iniciais são transferidos por download. Assim, ele reflete diretamente o tempo total de carregamento de todos os recursos críticos do site.5

As dimensões que escolhemos foram:

  • Nossa dimensão personalizada Status do service worker.
  • Tipo de usuário, que indica se esta é a primeira visita do usuário ao site ou se ele está retornando. Observação: um visitante novo não terá recursos armazenados em cache, mas um visitante recorrente pode ter.
  • Categoria do dispositivo, que permite comparar os resultados entre dispositivos móveis e computadores.

Para controlar a possibilidade de fatores não relacionados ao service worker estarem distorcendo os resultados do tempo de carregamento, limitamos nossa consulta para incluir apenas navegadores compatíveis com o service worker.

Como você pode ver, as visitas ao nosso app quando controladas por um service worker carregaram muito mais rápido do que as visitas não controladas, mesmo as de usuários recorrentes que provavelmente tinham a maioria dos recursos da página em cache. Também é interessante notar que, em média, os visitantes em dispositivos móveis com um worker de serviço tiveram carregamentos mais rápidos do que os novos visitantes em computadores.

"…as visitas ao nosso app quando controladas por um service worker são carregadas muito mais rápido do que as visitas não controladas…"

Confira mais detalhes nas duas tabelas a seguir:

Tempo médio de carregamento da página (computador)
Status do service worker Tipo de usuário Tempo médio de carregamento da página (ms) Tamanho da amostra
Controlou Visitante recorrente 2568 30860
Compatível Visitante recorrente 3612 1289
Compatível Novo visitante 4664 21991
Tempo médio de carregamento da página (dispositivos móveis)
Status do service worker Tipo de usuário Tempo médio de carregamento da página (ms) Tamanho da amostra
Controlou Visitante recorrente 3760 8162
Compatível Visitante recorrente 4843 676
Compatível Novo visitante 6158 5779

Talvez você esteja se perguntando como é possível que um visitante recorrente, cujo navegador ofereça suporte a service worker, esteja em um estado não controlado. Há algumas explicações possíveis para isso:

  • O usuário saiu da página na visita inicial antes que o service worker tivesse a chance de concluir a inicialização.
  • O usuário desinstalou o service worker usando as ferramentas para desenvolvedores.

Essas duas situações são relativamente raras. É possível conferir isso nos dados, analisando os valores da amostra de carregamento de página na quarta coluna. As linhas do meio têm uma amostra muito menor do que as outras duas.

Nossa segunda pergunta foi: Como o service worker afeta a experiência de carregamento do site?

Para responder a essa pergunta, criamos outro relatório personalizado para a métrica Valor médio do evento e filtramos os resultados para incluir apenas nossos eventos firstpaint. Usamos as dimensões Categoria de dispositivo e Status do service worker personalizada.

Ao contrário do que eu esperava, o service worker em dispositivos móveis teve muito menos impacto no tempo de primeira pintura do que no carregamento geral da página.

"…o service worker em dispositivos móveis teve muito menos impacto no tempo de primeira pintura do que no carregamento geral da página."

Para entender por que isso acontece, precisamos analisar melhor os dados. As médias podem ser boas para descrições gerais e amplas, mas, para ter uma ideia de como esses números se dividem entre vários usuários, precisamos analisar uma distribuição de firstpaint vezes.

Como conferir a distribuição de uma métrica no Google Analytics

Para conseguir a distribuição dos tempos de firstpaint, precisamos ter acesso aos resultados individuais de cada evento. Infelizmente, o Google Analytics não facilita isso.

O Google Analytics permite que você divida um relatório por qualquer dimensão, mas não por métricas. Isso não significa que é impossível, apenas que precisamos personalizar nossa implementação um pouco mais para conseguir o resultado desejado.

Como os resultados do relatório só podem ser divididos por dimensões, precisamos definir o valor da métrica (neste caso, o tempo firstpaint) como uma dimensão personalizada no evento. Para isso, criamos outra dimensão personalizada chamada Valor da métrica e atualizamos nossa lógica de acompanhamento firstpaint da seguinte maneira:

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

No momento, a interface da Web do Google Analytics não oferece uma maneira de visualizar a distribuição de valores de métricas arbitrárias, mas, com a ajuda da API Google Analytics Core Reporting e da biblioteca Google Charts, podemos consultar os resultados brutos e criar um histograma.

Por exemplo, a configuração de solicitação de API a seguir foi usada para receber uma distribuição de valores de firstpaint em computadores com um service worker não 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'
   
}
 
]
}

Essa solicitação de API retorna uma matriz de valores semelhante a esta (observe que esses são apenas os cinco primeiros resultados). Os resultados são classificados do menor para o maior, então essas linhas representam os tempos mais rápidos.

Resultados da resposta da API (primeiras cinco linhas)
ga:dimension2 ga:totalEvents
4 3
5 2
6 10
7 8
8 10

Confira o que esses resultados significam:

  • Houve 3 eventos em que o valor de firstpaint foi 4 ms
  • Houve dois eventos em que o valor de firstpaint foi 5 ms
  • Houve 10 eventos em que o valor de firstpaint foi 6 ms
  • Houve 8 eventos em que o valor de firstpaint foi 7 ms
  • Houve 10 eventos em que a firstpaint value foi de 8 ms
  • etc.

Com esses resultados, podemos extrapolar o valor de firstpaint para cada evento e criar um histograma da distribuição. Fizemos isso para cada uma das consultas que executamos.

Confira como a distribuição ficou no computador com um service worker não controlado (mas com suporte):

Tempo para a distribuição da primeira pintura no computador (com suporte)

O tempo mediano de firstpaint para a distribuição acima é 912 ms.

A forma dessa curva é bastante típica das distribuições de tempo de carregamento. Compare isso com o histograma abaixo, que mostra a distribuição de eventos de primeira pintura para visitas em que um service worker estava controlando a página.

Tempo para a distribuição da first paint no computador (controlado)

Quando um service worker controlava a página, muitos visitantes tiveram uma primeira pintura quase imediata, com uma mediana de 583 ms.

"…quando um service worker controlava a página, muitos visitantes tiveram uma primeira pintura quase imediata…"

Para ter uma ideia melhor de como essas duas distribuições se comparam, o próximo gráfico mostra uma visualização mesclada das duas. O histograma que mostra visitas não controladas do service worker é sobreposto ao histograma que mostra visitas controladas, e ambos são sobrepostos a um histograma que mostra os dois combinados.

Tempo de distribuição da first paint em computadores

Uma coisa que achei interessante nesses resultados foi que a distribuição com um service worker controlado ainda tinha uma curva em forma de sino após o pico inicial. Eu esperava um grande pico inicial e depois uma queda gradual, não um segundo pico na curva.

Quando procurei a causa disso, descobri que, embora um service worker possa controlar uma página, a linha de execução dele pode estar inativa. O navegador faz isso para economizar recursos. Obviamente, você não precisa que todos os service workers de todos os sites que você já visitou estejam ativos e prontos a qualquer momento. Isso explica a cauda da distribuição. Para alguns usuários, houve um atraso enquanto a linha de execução do worker de serviço era iniciada.

No entanto, como você pode ver na distribuição, mesmo com esse atraso inicial, os navegadores com service worker entregam conteúdo mais rápido do que os que passam pela rede.

Confira como ficou no dispositivo móvel:

Tempo para a distribuição da first paint em dispositivos móveis

Ainda tivemos um aumento considerável no tempo de primeira pintura quase imediato, mas a cauda era um pouco maior e mais longa. Isso provavelmente ocorre porque, em dispositivos móveis, a inicialização de uma linha de execução de worker de serviço ocioso leva mais tempo do que em computadores. Isso também explica por que a diferença entre o tempo médio de firstpaint não foi tão grande quanto eu esperava (discutido acima).

"…em dispositivos móveis, iniciar uma linha de execução de service worker ociosa leva mais tempo do que em computadores."

Confira o detalhamento dessas variações da mediana dos tempos de primeira pintura em dispositivos móveis e computadores agrupados por status do worker de serviço:

Tempo mediano para a primeira pintura (ms)
Status do service worker Computador Dispositivo móvel
Controlou 583 1634
Compatível (não controlado) 912 1933

Embora a criação dessas visualizações de distribuição tenha levado um pouco mais de tempo e esforço do que a criação de um relatório personalizado no Google Analytics, elas nos dão uma ideia muito melhor de como os workers de serviço afetam o desempenho do nosso site do que apenas as médias.

Outros impactos dos service workers

Além do impacto na performance, os service workers também afetam a experiência do usuário de várias outras maneiras que podem ser medidas com o Google Analytics.

Acesso off-line

Os service workers permitem que os usuários interajam com seu site off-line. Embora algum tipo de suporte off-line seja provavelmente essencial para qualquer app da Web progressivo, determinar a importância disso no seu caso depende em grande parte da quantidade de uso off-line. Mas como medir isso?

O envio de dados para o Google Analytics requer uma conexão de Internet, mas não é necessário que os dados sejam enviados no momento exato em que a interação ocorreu. O Google Analytics oferece suporte ao envio de dados de interação após o fato, especificando um deslocamento de tempo (pelo parâmetro qt).

Nos últimos dois anos, a IOWA tem usado um script de worker de serviço que detecta hits com falhas no Google Analytics quando o usuário está off-line e os reproduz mais tarde com o parâmetro qt.

Para acompanhar se o usuário estava on-line ou off-line, criamos uma dimensão personalizada chamada On-line e definimos o valor de navigator.onLine. Depois, detectamos os eventos online e offline e atualizamos a dimensão de acordo com isso.

Para ter uma ideia de como era comum um usuário ficar off-line ao usar a IOWA, criamos um segmento que segmentava usuários com pelo menos uma interação off-line. Eram quase 5% dos usuários.

Notificações push

Os service workers permitem que os usuários ativem o recebimento de notificações push. No IOWA, os usuários eram notificados quando uma sessão na programação estava prestes a começar.

Como em qualquer forma de notificação, é importante encontrar o equilíbrio entre oferecer valor ao usuário e incomodar. Para entender melhor o que está acontecendo, é importante acompanhar se os usuários estão ativando o recebimento dessas notificações, se estão interagindo com elas quando chegam e se os usuários que ativaram anteriormente mudaram a preferência e desativaram.

No IOWA, só enviamos notificações relacionadas à programação personalizada do usuário, algo que só os usuários conectados podem criar. Isso limitou o conjunto de usuários que podiam receber notificações aos usuários conectados (rastreados por uma dimensão personalizada chamada Signed In) cujos navegadores aceitavam notificações push (rastreadas por outra dimensão personalizada chamada Notification Permission).

O relatório a seguir é baseado na métrica Usuários e na nossa dimensão personalizada "Permissão de notificação", segmentada por usuários que fizeram login em algum momento e cujos navegadores aceitam notificações push.

É ótimo saber que mais da metade dos usuários que fizeram login optaram por receber notificações push.

Banners de instalação de apps

Se um Progressive Web App atender aos critérios e for usado com frequência por um usuário, um banner de instalação do app pode ser mostrado para que ele seja adicionado à tela inicial.

No IOWA, rastreamos a frequência com que essas solicitações foram mostradas ao usuário (e se foram aceitas) com o seguinte 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
   
});
 
});
});

Dos usuários que viram um banner de instalação de app, cerca de 10% escolheram adicioná-lo à tela inicial.

Possíveis melhorias no acompanhamento (para a próxima vez)

Os dados de análise que coletamos do IOWA este ano foram inestimáveis. Mas a retrospectiva sempre traz à tona falhas e oportunidades para melhorar as coisas na próxima vez. Depois de terminar a análise deste ano, aqui estão duas coisas que gostaríamos de ter feito de forma diferente e que os leitores que querem implementar uma estratégia semelhante podem considerar:

1. Acompanhar mais eventos relacionados à experiência de carregamento

Rastreamos vários eventos que correspondem a uma métrica técnica (por exemplo, HTMLImportsLoaded, WebComponentsReady etc.), mas, como grande parte do carregamento foi feita de forma assíncrona, o ponto em que esses eventos foram acionados não correspondia necessariamente a um momento específico na experiência de carregamento geral.

O evento principal relacionado ao carregamento que não rastreamos (mas gostaríamos de ter feito) é o ponto em que a tela de apresentação desaparece e o usuário pode ver o conteúdo da página.

2. Armazenar o ID do cliente do Google Analytics no IndexedDB

Por padrão, a analytics.js armazena o campo de ID do cliente nos cookies do navegador. Infelizmente, os scripts de service worker não podem acessar cookies.

Isso foi um problema para nós quando tentamos implementar o acompanhamento de notificações. Queríamos enviar um evento do worker de serviço (pelo protocolo de medição) toda vez que uma notificação fosse enviada a um usuário e, em seguida, acompanhar o sucesso do reengajamento dessa notificação se o usuário clicasse nela e voltasse ao app.

Embora tenhamos conseguido acompanhar o sucesso das notificações em geral pelo parâmetro da campanha utm_source, não foi possível vincular uma sessão de reengajamento específica a um usuário específico.

Para contornar essa limitação, poderíamos armazenar o ID do cliente usando o IndexedDB no nosso código de rastreamento. Assim, esse valor seria acessível ao script do service worker.

3. Permitir que o service worker informe o status on-line/off-line

A inspeção de navigator.onLine informa se o navegador consegue se conectar ao roteador ou à rede local, mas não necessariamente informa se o usuário tem conectividade real. Como nosso script de service worker de análise off-line simplesmente reproduzia os hits com falhas (sem modificá-los ou marcá-los como falhas), provavelmente não estávamos informando corretamente nosso uso off-line.

No futuro, vamos acompanhar o status de navigator.onLine e se o hit foi reproduzido pelo service worker devido a uma falha inicial de rede. Isso vai nos dar uma ideia mais precisa do uso off-line real.

Conclusão

Este estudo de caso mostrou que o uso do service worker melhorou a performance de carregamento da WebApp da Google I/O em uma ampla variedade de navegadores, redes e dispositivos. Também mostra que, ao analisar uma distribuição dos dados de carga em uma ampla gama de navegadores, redes e dispositivos, você tem muito mais insights sobre como essa tecnologia lida com cenários do mundo real e descobre características de desempenho que talvez não esperasse.

Confira alguns dos principais pontos do estudo IOWA:

  • Em média, as páginas carregavam muito mais rápido quando um service worker controlava a página do que sem um service worker, tanto para visitantes novos quanto recorrentes.
  • As visitas a páginas controladas por um service worker eram carregadas quase instantaneamente para muitos usuários.
  • Os service workers, quando inativos, demoravam um pouco para inicializar. No entanto, um service worker inativo ainda tem um desempenho melhor do que nenhum service worker.
  • O tempo de inicialização de um worker de serviço inativo foi maior em dispositivos móveis do que em computadores.

Embora os ganhos de desempenho observados em um aplicativo específico sejam geralmente úteis para informar à comunidade de desenvolvedores, é importante lembrar que esses resultados são específicos do tipo de site do IOWA (um site de eventos) e do tipo de público-alvo do IOWA (principalmente desenvolvedores).

Se você estiver implementando um worker de serviço no seu app, é importante implementar sua própria estratégia de medição para avaliar sua performance e evitar regressões futuras. Se fizer isso, compartilhe seus resultados para que todos possam se beneficiar.

Notas de rodapé

  1. Não é totalmente justo comparar o desempenho da implementação do cache do service worker com o desempenho do site com o cache HTTP. Como estávamos otimizando a IOWA para o service worker, não gastamos muito tempo otimizando para o cache HTTP. Se tivéssemos, os resultados provavelmente seriam diferentes. Para saber mais sobre como otimizar seu site para o cache HTTP, consulte Como otimizar o conteúdo de maneira eficiente.
  2. Dependendo de como o site carrega os estilos e o conteúdo, é possível que o navegador pinte antes que o conteúdo ou os estilos estejam disponíveis. Nesses casos, firstpaint pode corresponder a uma tela branca em branco. Se você usar firstpaint, é importante garantir que ele corresponda a um ponto significativo no carregamento dos recursos do site.
  3. Tecnicamente, poderíamos enviar um hit de cronometragem (que não é uma interação por padrão) para capturar essas informações em vez de um evento. Na verdade, os dados de tempo foram adicionados ao Google Analytics especificamente para acompanhar métricas de carga como essa. No entanto, os dados de tempo são amostrados em grande quantidade no momento do processamento, e os valores deles não podem ser usados em segmentos. Considerando essas limitações atuais, os eventos sem interação continuam sendo mais adequados.
  4. Para entender melhor o escopo de uma dimensão personalizada no Google Analytics, consulte a seção Dimensão personalizada da Central de Ajuda do Google Analytics. Também é importante entender o modelo de dados do Google Analytics, que consiste em usuários, sessões e interações (hits). Para saber mais, assista a aula da Analytics Academy sobre o modelo de dados do Google Analytics.
  5. Isso não considera os recursos carregados de forma lenta após o evento de carregamento.