Um dos benefícios mais significativos dos service workers (pelo menos do ponto de vista do 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 web app Google I/O (IOWA, na sigla em inglês) é um Progressive Web App que aproveita a maioria dos novos recursos oferecidos pelos service workers para oferecer aos usuários uma experiência rica, parecida com um app. A empresa também usou o Google Analytics para capturar os principais dados de performance e os 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 worker de serviço tem melhor desempenho do que os mecanismos de armazenamento em cache HTTP disponíveis em todos os navegadores?
Já esperamos que as páginas sejam carregadas mais rapidamente para os visitantes recorrentes do que para os novos, pois os navegadores podem armazenar as 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 nosso service worker para que todos os recursos fossem armazenados em cache, assim 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, rastrear quanto tempo a tela de apresentação leva para aparecer faz muito mais sentido como uma forma de medir o desempenho de carregamento percebido. Escolhemos a métrica tempo da primeira pintura para chegar a esse valor.
Depois de decidir 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 da análise de dados
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 quiser acompanhar outras interações do usuário, você terá que fazê-lo 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 registrar o tempo da first paint
Alguns navegadores registram o momento exato em que o primeiro pixel é pintado na tela e disponibilizam esse tempo 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 sem interação com o tempo da first paint como seu 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 sempre executado após a first paint, adiamos a chamada para sendTimeToFirstPaint()
para 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 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 do IOWA, isso também significa que todos os recursos foram armazenados em cache e que a página funciona off-line.
- Suportado: o navegador oferece suporte ao service worker, mas ele ainda não controla 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 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, 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ó associa o status do service worker a esse evento específico. Como o status do service worker pode ser útil para qualquer interação, é melhor incluir esse dado 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.), 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 da função getServiceWorkerStatus()
e 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 do 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, pudemos relatar esses dados para ver 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 tivemos foi: o armazenamento em cache do service worker tem um desempenho melhor do que os mecanismos de cache HTTP 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
é acionado somente depois que todos os recursos iniciais são transferidos por download. Assim, ele reflete diretamente o tempo de carregamento total 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 é a primeira visita do usuário ao site ou se ele está retornando. Observação: um novo visitante não terá recursos em cache, ao contrário de um visitante recorrente.
- 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 distorcerem nossos resultados de tempo de carregamento, limitamos a consulta para incluir apenas navegadores que oferecem suporte ao 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 rapidamente 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 |
Você pode estar se perguntando como é possível que um visitante recorrente cujo navegador ofereça suporte ao service worker fique 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. Nos dados, podemos ver isso ao observar os valores de 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 dividir um relatório por qualquer dimensão desejada, mas não permite dividir um relatório 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 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 que se parecem com o exemplo a seguir. 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 da API (primeiras 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 o
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):
O tempo médio de firstpaint
para a distribuição acima é de 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.
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 entender melhor 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.
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 esperava um segundo pico na curva.
Ao analisar o que poderia estar causando isso, 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:
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 criar essas visualizações de distribuição exigisse um pouco mais de tempo e esforço do que criar um relatório personalizado no Google Analytics, elas nos dão uma noção muito melhor de como os service workers afetam o desempenho do nosso site do que as médias sozinhos.
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 o nível de importância disso no seu caso depende principalmente 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 ativam o recebimento dessas notificações, se estão interagindo com elas quando chegam e se algum usuário que já ativou mudou as preferências ou desistiu.
No IOWA, enviamos apenas notificações relacionadas à programação personalizada do usuário, algo que apenas usuários conectados poderiam 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 ofereciam suporte a notificações push (rastreadas por outra dimensão personalizada chamada Notification Permission).
O relatório a seguir tem como base a métrica Usuários e nossa dimensão personalizada "Permissão de notificação", segmentada por usuários que fizeram login em algum momento e com navegadores compatíveis com 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 poderá ser mostrado, 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 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 os buracos e as oportunidades de melhorar as coisas para a próxima vez. Depois de terminar a análise deste ano, aqui estão duas coisas que eu gostaria 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 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 rastrear 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 estávamos subestimando 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 nos proporciona uma imagem mais precisa do verdadeiro uso off-line.
Conclusão
Este estudo de caso mostrou que o uso do worker de serviço realmente 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é
- 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 o 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 cache HTTP, leia Como otimizar conteúdo com eficiência.
- 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 em branco. Se você usarfirstpaint
, é importante garantir que ele corresponda a um ponto significativo no carregamento dos recursos do site. - 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.
- Para entender melhor em qual escopo atribuir 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.
- Isso não inclui os recursos com carregamento lento após o evento de carregamento.