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

Um dos benefícios mais significativos dos service workers (pelo menos de uma perspectiva 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 deve carregar muito mais rápido para os visitantes que retornam. Mas o que esses ganhos realmente significam para os usuários reais? E como isso pode ser medido?

O app da Web do Google I/O (abreviada como IOWA, na sigla em inglês) é um Progressive Web App que utilizou a maioria dos novos recursos oferecidos pelos service workers para oferecer aos usuários uma experiência avançada como a do app. Ela também usou o Google Analytics para capturar os principais dados de desempenho e padrões de uso do seu grande e diversificado público-alvo.

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

Comece com as perguntas

Sempre que você implementar análises em um site ou aplicativo, é importante começar identificando as perguntas que você está tentando responder com base nos dados que vai coletar.

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

1. O armazenamento em cache do service worker é mais eficiente do que os mecanismos de cache HTTP existentes disponíveis em todos os navegadores?

Já esperamos que as páginas carreguem mais rapidamente para os visitantes recorrentes do que para os novos visitantes, pois os navegadores podem armazenar solicitações em cache e veiculá-las instantaneamente em acessos repetidos.

Os service workers oferecem recursos alternativos de armazenamento em cache que oferecem aos desenvolvedores controle refinado sobre o que exatamente e como o armazenamento em cache é feito. Na IOWA, otimizamos nossa 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? E em caso positivo, em quanto melhor? 1

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

Em outras palavras, qual é a rapidez do carregamento do site, independentemente dos tempos de carregamento reais medidos pelas métricas tradicionais de carregamento de página?

Responder perguntas sobre como uma experiência é obviamente não é uma tarefa fácil, e nenhuma métrica vai representar perfeitamente um sentimento subjetivo. Dito isso, definitivamente há algumas métricas que são melhores do que outras, portanto, é importante escolher as corretas.

Como escolher a métrica certa

Por padrão, o Google Analytics acompanha os tempos de carregamento da página (com a API Navigation Timing) de 1% dos visitantes do site e disponibiliza esses dados com métricas como o tempo médio de carregamento da página.

O Tempo médio de carregamento da página é uma boa métrica para responder à primeira pergunta, mas não para responder à segunda. Por um lado, 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 ter a sensação de carregar de maneira muito diferente. Por exemplo, um site com uma tela de apresentação ou um indicador de carregamento provavelmente vai parecer muito mais rápido do que um que só mostra uma página em branco por vários segundos.

No IOWA, mostramos uma animação de contagem regressiva da 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, rastrear quanto tempo a tela de apresentação leva para aparecer faz muito mais sentido como uma maneira de medir o desempenho de carregamento percebido. Escolhemos a métrica tempo da primeira exibição para receber esse valor.

Depois de escolher as perguntas que queríamos responder e identificar as métricas que seriam úteis para respondê-las, era hora de implementar o Google Analytics e começar a avaliar.

Implementação da análise

Se você já usou o Google Analytics, provavelmente já conhece o snippet de acompanhamento JavaScript recomendado. Ela é assim:

<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 no código acima inicializa uma função ga() global (se ela ainda não existir), e a última linha faz o download da biblioteca analytics.js de forma assíncrona.

A parte do meio contém estas duas linhas:

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

Esses dois comandos acompanham quais páginas são visitadas pelas pessoas que acessam seu site, mas não muito mais que isso. Se você quiser acompanhar outras interações dos usuários, deverá fazê-lo por conta própria.

Para a IOWA, queríamos acompanhar mais dois itens:

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

Como registrar o tempo da primeira exibição

Alguns navegadores registram o momento exato em que o primeiro pixel é pintado na tela e disponibilizam esse tempo para os desenvolvedores. Em comparação com o valor navigationStart exposto pela API Navigation Timing, isso nos dá uma contagem muito precisa de quanto tempo se passou entre o momento em que o usuário solicitou a página pela primeira vez e quando viu algo pela primeira vez.

Como já mencionei, o tempo até a primeira exibição é uma métrica importante a ser medida, pois é o primeiro ponto em que o usuário visualiza a velocidade de carregamento do seu 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 conseguir o primeiro valor de exibição nos navegadores que a expõem, criamos a função utilitária 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 sem interação, com o tempo da primeira exibição 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 acompanhamento ficará 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();

Observe que, dependendo de quando o código acima é executado, os pixels podem ou não ter sido pintados na tela. Para garantir que sempre executemos esse código após a primeira exibição, adiamos a chamada para sendTimeToFirstPaint() até o evento load. Na verdade, decidimos adiar o envio de todos os dados de análise para que a página fosse carregada depois do carregamento dessa página 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 rastrear o status do service worker. Caso contrário, não poderíamos comparar os primeiros tempos de exibição de uma página controlada pelo service worker e uma página não controlada.

Como determinar o status do service worker

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

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

Essa função recebeu o status de service worker para nós. A próxima etapa foi associar esse status aos dados que estávamos enviando ao Google Analytics.

Acompanhamento de dados personalizados com dimensões personalizadas

Por padrão, o Google Analytics oferece várias maneiras de subdividir 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 importantes são Navegador, Sistema operacional ou Categoria do dispositivo.

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

Para IOWA, criamos uma dimensão personalizada chamada Service Worker Status e definimos o escopo como hit (por exemplo, por interação).4 Cada dimensão personalizada que você cria no Google Analytics recebe um índice exclusivo nessa propriedade. No seu código de acompanhamento, você pode 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ó associará o status do service worker a esse evento específico. Como o Status do service worker é potencialmente útil para saber em qualquer interação, é melhor incluí-lo em 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.), configuramos o valor da dimensão personalizada no próprio objeto tracker antes de enviar os dados 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 da função getServiceWorkerStatus() e será definido no objeto do rastreador.

Uma observação rápida sobre clareza e 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 mapeie 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 mencionado, enviar a dimensão Status do service worker com cada hit nos permite usá-la ao gerar relatórios sobre qualquer métrica.

Quase 85% de todas as visualizações de página de IOWA eram de navegadores compatíveis com o service worker.

Resultados: respostas às nossas perguntas

Quando começamos a coletar dados para responder às nossas perguntas, poderíamos gerar um relatório sobre esses dados para ver os resultados. Observação: todos os dados do Google Analytics mostrados aqui representam o tráfego real da Web para o site da IOWA de 16 a 22 de maio de 2016.

Nossa primeira pergunta foi: o armazenamento em cache do service worker é mais eficiente do que os mecanismos de cache HTTP existentes disponíveis em todos os navegadores?

Para responder a essa pergunta, criamos um relatório personalizado que analisava 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 é disparado somente após o download de todos os recursos iniciais. Portanto, ele reflete diretamente o tempo total de carregamento de todos os recursos essenciais do site.5

As dimensões que escolhemos foram:

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

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

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

"...visitas ao nosso aplicativo quando controladas por um service worker carregado um pouco mais rápido do que 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

Você pode estar se perguntando como é possível que um visitante antigo com suporte ao service worker fique em um estado não controlado. Existem algumas explicações possíveis para isso:

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

Ambas as situações são relativamente raras. Podemos ver isso nos dados observando os valores Amostra de carregamento de página na quarta coluna. Observe que 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 os eventos firstpaint. Usamos as dimensões Categoria do dispositivo e nossa dimensão personalizada Status do service worker.

Ao contrário do que eu esperava, o service worker no dispositivo móvel teve muito menos impacto no tempo da primeira exibição do que no carregamento geral da página.

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

Para entender por que isso acontece, precisamos nos aprofundar nos dados. As médias podem ser boas para visões gerais gerais e traços amplos, mas, para realmente ter uma noção de como esses números são divididos entre uma faixa de usuários, precisamos analisar uma distribuição de firstpaint vezes.

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

Para distribuir firstpaint vezes, precisamos de acesso aos resultados individuais de cada evento. Infelizmente, o Google Analytics não torna isso fácil.

Com o Google Analytics, é possível detalhar um relatório por qualquer dimensão, mas não por métricas. Isso não quer dizer que seja impossível, apenas significa que tivemos que personalizar um pouco mais nossa implementação para chegar ao resultado desejado.

Como os resultados do relatório só podem ser detalhados por dimensões, tivemos que definir o valor da métrica (neste caso, firstpaint vez) 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. No entanto, com a ajuda da API de relatórios principais do Google Analytics e da biblioteca de gráficos do Google, podemos consultar os resultados brutos e depois criar um histograma.

Por exemplo, a configuração de solicitação de API a seguir foi usada para ter uma distribuição de valores firstpaint no computador 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 com esta aparência (observação: esses são apenas os cinco primeiros resultados). Os resultados são classificados do menor para o maior, de modo que essas linhas representam os tempos mais rápidos.

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

Veja o que esses resultados significam em inglês:

  • Houve três eventos em que o valor de firstpaint era de 4 ms
  • Houve dois eventos em que o valor de firstpaint era de 5 ms
  • Houve 10 eventos em que o valor de firstpaint era de 6 ms
  • Houve 8 eventos em que o valor de firstpaint era 7 ms
  • Houve 10 eventos em que a duração value do firstpaint foi de 8 ms
  • etc.

Com base nesses 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.

Esta é a aparência da distribuição em computadores com um service worker não controlado (mas com suporte):

Tempo para a distribuição da primeira exibição no computador (compatível)

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

O formato dessa curva é bastante típico de distribuições de tempo de carregamento. Compare isso com o histograma abaixo, que mostra a distribuição dos eventos da primeira exibição para visitas em que um service worker estava controlando a página.

Distribuição do tempo da primeira exibição no computador (controlado)

Quando um service worker estava controlando a página, muitos visitantes tiveram uma primeira pintura quase imediata, com uma média de 583 ms.

"... quando um service worker estava controlando a página, muitos visitantes experimentaram uma primeira pintura quase imediata..."

Para você ter uma noção 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 as visitas não controladas do service worker é sobreposto ao histograma mostrando visitas controladas, e ambos são sobrepostos a um histograma que mostra as duas combinadas.

Tempo para a distribuição da primeira exibição no computador

Uma coisa que achei interessante sobre esses 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 trilha gradual, não esperava um segundo pico na curva.

Ao analisar o que poderia estar causando isso, aprendi que, mesmo que um service worker possa estar controlando uma página, sua linha de execução pode estar inativa. O navegador faz isso para economizar recursos. Obviamente, você não precisa que todos os service workers de todos os sites visitados estejam ativos e prontos a qualquer momento. Isso explica a cauda da distribuição. Para alguns usuários, houve um atraso na inicialização da linha de execução do service worker.

Como é possível ver na distribuição, no entanto, mesmo com esse atraso inicial, os navegadores com service worker entregaram conteúdo mais rapidamente do que os navegadores que passam pela rede.

Veja como as coisas ficam em um dispositivo móvel:

Distribuição do tempo da primeira exibição em dispositivos móveis

Embora ainda tenhamos tido um aumento considerável nos tempos da primeira exibição quase imediatos, a cauda foi um pouco maior e mais longa. Isso pode acontecer porque, em dispositivos móveis, iniciar uma linha de execução ociosa do service worker leva mais tempo do que em um computador. 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 um computador."

Veja um detalhamento dessas variações dos tempos médios da primeira exibição em dispositivos móveis e computadores, agrupados por status de service worker:

Tempo médio até a primeira exibição (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 exigido um pouco mais de tempo e esforço do que a criação de um relatório personalizado no Google Analytics, elas nos deram uma noção muito melhor de como os service workers afetam o desempenho do nosso site do que as médias.

Outro impacto dos service workers

Além do impacto no desempenho, 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 enquanto estiverem off-line. Embora algum tipo de suporte off-line seja provavelmente essencial para qualquer Progressive Web App, determinar a importância dele no seu caso depende, em grande parte, da quantidade de uso que ocorre off-line. Mas como medimos isso?

O envio de dados ao Google Analytics requer uma conexão de Internet, mas não é necessário que os dados sejam enviados no horário exato em que a interação ocorreu. O Google Analytics permite o envio de dados de interação após a especificação de um ajuste de horário (usando o parâmetro qt).

Nos últimos dois anos, a IOWA vem usando um script de service worker que detecta hits com falha 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 a definimos com o valor de navigator.onLine. Em seguida, detectamos os eventos online e offline e atualizamos a dimensão de acordo.

Para entender 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. Acontece que isso foi 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 acontece com qualquer forma de notificação, é importante encontrar o equilíbrio entre fornecer valor ao usuário e irritar com ele. Para entender melhor o que está acontecendo, é importante acompanhar se os usuários ativam o recebimento dessas notificações, se estão interagindo com elas quando chegam e se algum usuário que já aceitou vai mudar de preferência e desativá-las.

No IOWA, enviamos apenas notificações relacionadas à programação personalizada do usuário, algo que apenas os usuários conectados poderiam criar. Isso limitava o conjunto de usuários que podiam receber notificações para usuários conectados (rastreados por uma dimensão personalizada chamada Conectado) com navegadores compatíveis com notificações push (rastreadas por outra dimensão personalizada chamada Permissão de notificação).

O relatório a seguir tem como base a métrica Usuários e nossa dimensão personalizada "Permissão de notificação", segmentado por usuários que fizeram login em algum momento e com navegadores compatíveis com notificações push.

É ótimo ver que mais da metade dos nossos usuários conectados optaram por receber notificações push.

Banners de instalação de apps

Se um app da Web de progresso atender aos critérios e for usado com frequência por um usuário, um banner de instalação de apps poderá aparecer solicitando que ele adicione o app à tela inicial.

No IOWA, rastreamos a frequência com que essas solicitações foram mostradas ao usuário (e se elas 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 aplicativo, cerca de 10% optaram por adicioná-lo à tela inicial.

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

Os dados analíticos que coletamos da IOWA neste ano foram inestimáveis. Mas a retrospectiva sempre traz problemas e oportunidades para melhorar as coisas para a próxima vez. Depois de terminar a análise deste ano, aqui estão duas coisas que eu gostaria que tivéssemos feito diferente e que leitores que queriam implementar uma estratégia semelhante poderiam 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.). No entanto, como grande parte do carregamento foi feito de modo assíncrono, o ponto em que esses eventos são disparados não correspondeu necessariamente a um momento específico na experiência de carregamento geral.

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

2. Armazenar o ID do cliente de análise no IndexedDB

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

Isso apresentou um problema quando tentamos implementar o rastreamento de notificações. Queríamos enviar um evento do service worker (pelo Measurement Protocol) sempre 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 possamos acompanhar o sucesso das notificações em geral com o parâmetro de campanha utm_source, não foi possível vincular uma sessão de reengajamento específica a um usuário específico.

O que poderíamos ter feito para contornar essa limitação foi armazenar o ID do cliente via IndexedDB em nosso código de rastreamento, e então esse valor seria acessível para o 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 de área local, mas não indica necessariamente se o usuário tem conectividade real. E como o script do service worker de análise off-line simplesmente reproduziu os hits com falha (sem modificá-los ou marcá-los como falhas), provavelmente estávamos subestimando o uso off-line.

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

Conclusão

Este estudo de caso mostrou que o uso do service worker realmente melhorou o desempenho de carregamento do app da Web do Google I/O em uma ampla variedade de navegadores, redes e dispositivos. Ele também demonstrou que, ao observar a distribuição dos dados de carregamento em uma ampla variedade de navegadores, redes e dispositivos, você tem muito mais insights sobre como essa tecnologia lida com situações reais e descobre características de desempenho que talvez não fossem esperadas.

Aqui estão algumas das principais conclusões do estudo da IOWA:

  • Em média, as páginas carregam um pouco mais rápido quando um service worker está controlando a página do que sem um service worker, para visitantes novos e recorrentes.
  • Visitas a páginas controladas por um service worker com carregamento quase instantâneo para muitos usuários.
  • Os service workers, quando inativos, levavam algum tempo para serem iniciados. No entanto, um service worker inativo ainda teve um desempenho melhor do que nenhum service worker.
  • O tempo de inicialização de um service worker 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 maior de desenvolvedores, é importante lembrar que esses resultados são específicos para o tipo de site da IOWA (um site de evento) e o tipo de público que a IOWA tem (principalmente desenvolvedores).

Se você estiver implementando um service worker no seu aplicativo, é importante implementar uma estratégia de medição própria para que você possa avaliar o próprio desempenho e evitar regressões futuras. Se sim, compartilhe seus resultados para que todos possam se beneficiar.

Notas de rodapé

  1. Não é justo comparar o desempenho da implementação do cache do nosso service worker com o desempenho do site somente com cache HTTP. Como estávamos otimizando IOWA para o service worker, não passamos muito tempo otimizando para cache HTTP. Se tivéssemos sido, os resultados provavelmente seriam diferentes. Para saber mais sobre como otimizar seu site para o cache HTTP, leia 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 consiga pintar o conteúdo antes da disponibilização dele. Nesses casos, firstpaint pode corresponder a uma tela em branco. Caso use firstpaint, é importante garantir que ele corresponda a um ponto significativo no carregamento dos recursos do seu site.
  3. Tecnicamente, poderíamos enviar um hit de tempo (que não são interação por padrão) para capturar essas informações em vez de um evento. Na verdade, os hits de velocidade foram adicionados ao Google Analytics especificamente para acompanhar métricas de carregamento como essa. No entanto, os hits de velocidade recebem muitas amostras no momento do processamento, e seus valores não podem ser usados em segmentos. Devido a essas limitações atuais, os eventos sem interação continuam sendo mais adequados.
  4. Para entender melhor qual escopo fornecer a 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 à aula da Analytics Academy sobre o modelo de dados do Google Analytics.
  5. Não são considerados recursos que demoram para carregar após o evento de carregamento.