Como medir métricas de performance críticas com o Google Analytics

Neste codelab, você vai aprender a usar o Google Analytics e a API User Timings para medir a performance real do seu site ou aplicativo e otimizá-la para melhorar a experiência dos usuários.

Ferramentas como WebPagetest.org são um ótimo começo para otimizações de desempenho, mas o verdadeiro teste de desempenho do site sempre será dados reais de usuários reais.

Se você tem um site, é provável que já use o Google Analytics para medir o tráfego e o uso de dispositivos e navegadores. Com um pouco mais de código, você pode adicionar métricas de performance.

O que você vai aprender

  • Como medir métricas de performance com precisão e eficácia usando a API User Timings
  • Como enviar esses dados ao Google Analytics para que eles possam ser incorporados aos seus relatórios

O que é necessário

Como você usará este tutorial?

Apenas leitura Leitura e exercícios

Como você classificaria sua experiência com a criação de sites ou aplicativos?

Iniciante Intermediário Proficiente

Você pode fazer o download de todo o exemplo de código para seu computador...

Fazer o download do ZIP

...ou clone o repositório do GitHub pela linha de comando.

git clone https://github.com/googlecodelabs/performance-analytics.git

O exemplo de código é dividido em subdiretórios que correspondem a cada uma das etapas numeradas deste codelab. Você pode usar isso para pular facilmente no codelab ou verificar se a implementação está correta.

Se você tiver acesso a um programa de diferenciação, use-o para ver exatamente o que mudou de uma etapa para outra.

Neste codelab, você vai usar um único arquivo HTML que carrega os seguintes recursos:

  • Fontes da Web
  • Folhas de estilo
  • Imagens
  • JavaScript

Você vai escrever um novo código que mede as principais métricas de performance de cada um desses tipos de recursos.

Considerações sobre a performance dos recursos

Se você já leu algo sobre otimização de performance, provavelmente sabe que cada um desses tipos de recursos tem suas próprias peculiaridades e pode afetar a performance geral percebida de várias maneiras.

CSS

Por exemplo, as folhas de estilo bloqueiam a renderização de todos os elementos no DOM que vêm depois delas. Isso significa que o navegador precisa fazer uma solicitação para a folha de estilo, fazer o download e analisar antes de renderizar qualquer conteúdo no DOM que venha depois dela. Por isso, geralmente é melhor colocar as folhas de estilo no <head> do documento. Devido à natureza de bloqueio do CSS, também é recomendável colocar apenas o CSS crítico no <head> e carregar o CSS não crítico de forma assíncrona depois.

JavaScript

O JavaScript, por outro lado, não bloqueia a renderização, mas bloqueia a análise e a construção do DOM. Isso é necessário porque o JavaScript pode modificar o DOM. Isso significa que, sempre que o navegador encontra uma tag <script> (exceto scripts assíncronos), ele precisa executar o código antes de continuar para a próxima tag. Se a tag <script> fizer referência a um arquivo JavaScript externo, ela precisará baixar e executar o código antes de continuar.

Por isso, geralmente é recomendável que o JavaScript seja carregado logo antes da tag de fechamento </body>, para que a maior parte do DOM esteja disponível o mais rápido possível.

Fontes da Web

Se você carregar fontes da Web, também poderá bloquear a renderização do documento até que a fonte esteja disponível para uso. Nesse caso, é fundamental entender quanto tempo isso leva para os usuários. Uma fonte da Web pode carregar rapidamente para você, mas muito lentamente para a maioria das pessoas que visitam seu site. Por isso, é tão importante medir e tomar decisões com base em dados reais.

Imagens

Por fim, as imagens podem dar vida a um site, mas também costumam demorar mais para carregar. Entender o que isso significa e identificar correlações entre padrões de uso e tempos de carregamento da página é fundamental para saber como otimizar.

A primeira etapa deste codelab é conferir como a página de demonstração aparece antes de adicionar qualquer código de medição de performance.

Para ver a demonstração, crie uma pasta e adicione um arquivo chamado index.html dentro dela. Em seguida, copie e cole o código abaixo no arquivo index.html.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Performance Analytics Demo</title>

  <!-- Start fonts -->
  <link href="https://fonts.googleapis.com/css?family=Roboto:400,700,400italic" rel="stylesheet">
  <!-- End fonts -->

  <!-- Start CSS -->
  <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
  <style>
    body { font-family: Roboto, sans-serif; margin: 1em; }
    img { float: left; height: auto; width: 33.33%; }
    .gallery { overflow: hidden; }
  </style>
  <!-- End CSS -->

</head>
<body>

  <div class="container">

    <!-- Start images -->
    <div class="gallery">
      <img src="http://lorempixel.com/380/200/animals/1/">
      <img src="http://lorempixel.com/380/200/animals/2/">
      <img src="http://lorempixel.com/380/200/animals/3/">
    </div>
    <!-- End images -->

    <h1>Performance Analytics Demo</h1>
    <p>Real performance data from real users.</p>

  </div>

  <!-- Start JavaScript -->
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
  <!-- End JavaScript -->

</body>
</html>

Em seguida, abra o Web Server para Chrome e inicie um servidor local no diretório que você acabou de criar. Confira se a opção "Mostrar automaticamente index.html" está marcada.

Screen Shot 2016-05-11 at 1.03.43 PM.png

Agora você pode navegar até http://127.0.0.1:8887/ no navegador e ver o arquivo de demonstração. O código será semelhante a este:

Screen Shot 2016-05-11 at 10.59.03 AM.png

Depois que a página de demonstração estiver em execução, analise o código e confira todos os tipos de recursos que estão sendo carregados. Nas próximas etapas, você vai adicionar código para medir quando esses recursos são carregados e podem ser usados pelo usuário.

Conforme mencionado na seção de considerações sobre a performance de recursos, o CSS bloqueia a renderização de elementos do DOM e a execução de scripts que vêm depois dele no DOM.

O arquivo de demonstração que você acabou de criar contém o seguinte CSS, que faz referência ao Bootstrap e a alguns estilos inline.

<!-- Start CSS -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
<style>
  body { font-family: Roboto, sans-serif; margin: 1em; }
  img { float: left; height: auto; width: 33.33%; }
  .gallery { overflow: hidden; }
</style>
<!-- End CSS -->

Como o CSS bloqueia a renderização de elementos DOM e a execução de scripts, é possível determinar quando o CSS termina de bloquear adicionando uma tag <script> imediatamente após o CSS que armazena a hora atual.

Você pode fazer isso criando uma variável e atribuindo new Date() a ela, mas, graças à API User Timings, há uma maneira muito mais fácil: o método performance.mark.

Para marcar quando o CSS terminar de bloquear a renderização e a execução de scripts, adicione a seguinte linha de código imediatamente antes do comentário de fechamento <!-- End CSS -->.

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

O método performance.mark cria um carimbo de data/hora de alta resolução nesse momento exato e o associa a qualquer nome transmitido ao método. Neste caso, você nomeou a marca como "css:unblock".

O método performance.mark funciona em conjunto com o método performance.measure, que é usado para calcular a diferença de tempo entre duas marcações. Além das marcações que você faz, também é possível usar as que o navegador faz automaticamente para os vários pontos na API Navigation Timing.

A função utilitária a seguir mede e retorna a duração entre uma marca adicionada e a marca responseEnd criada pela API Navigation Timing.

function measureDuration(mark, opt_reference) {
  var reference = opt_reference || 'responseEnd';
  var name = reference + ':' + mark;

  // Clears any existing measurements with the same name.
  performance.clearMeasures(name);

  // Creates a new measurement from the reference point to the specified mark.
  // If more than one mark with this name exists, the most recent one is used.
  performance.measure(name, reference, mark);

  // Gets the value of the measurement just created.
  var measure = performance.getEntriesByName(name)[0];

  // Returns the measure duration.
  return measure.duration;
}

Para começar a usar essa função utilitária, crie um arquivo chamado perf-analytics.js (no mesmo diretório do arquivo index.html) e copie e cole o código acima nele.

Agora que essa função está definida, você pode chamá-la e transmitir o nome da marca "css:unblock". Para não interferir no carregamento de outros recursos, adie a execução dessas medições até depois que o evento de carregamento da janela for acionado.

Depois de escrever uma função para chamar esse código, seu arquivo perf-analytics.js vai ficar parecido com isto:

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


/**
 * Calculates the time duration between the responseEnd timing event and when
 * the CSS stops blocking rendering, then logs that value to the console.
 */
function measureCssUnblockTime() {
  console.log('CSS', 'unblock', measureDuration('css:unblock'));
}


/**
 * Accepts a mark name and an optional reference point in the navigation timing
 * API and returns the time duration between the reference point and the last
 * mark (chronologically).
 * @param {string} mark The mark name.
 * @param {string=} opt_reference An optional reference point from the
 *     navigation timing API. Defaults to 'responseEnd'.
 * @return {number} The time duration
 */
function measureDuration(mark, opt_reference) {
  var reference = opt_reference || 'responseEnd';
  var name = reference + ':' + mark;

  // Clears any existing measurements with the same name.
  performance.clearMeasures(name);

  // Creates a new measurement from the reference point to the specified mark.
  // If more than one mark with this name exists, the most recent one is used.
  performance.measure(name, reference, mark);

  // Gets the value of the measurement just created.
  var measure = performance.getEntriesByName(name)[0];

  // Returns the measure duration.
  return measure.duration;
}

Por fim, carregue o script perf-analytics.js de index.html. Para fazer isso, adicione a seguinte tag de script ao documento principal. Adicione-o por último para que não interfira no carregamento de outros recursos.

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

Depois de concluir esta etapa, seu código vai corresponder ao que está no diretório 01-css do repositório do codelab.

Se você carregar a página em um navegador e abrir o console do desenvolvedor, verá algo como a saída a seguir:

Screen Shot 2016-05-17 at 11.13.02 AM.png

As fontes da Web geralmente são carregadas por uma folha de estilo externa, como visto no arquivo de demonstração inicial:

<!-- Start fonts -->
<link href="https://fonts.googleapis.com/css?family=Roboto:400,700,400italic" rel="stylesheet">
<!-- End fonts -->

Como essa é uma tag <link> para um arquivo CSS, pode parecer que determinar quando as fontes são carregadas e estão prontas para uso é tão simples quanto adicionar uma marca dentro de uma tag <script> imediatamente após a <link>, assim como na etapa 1.

Infelizmente, não é tão simples assim.

As folhas de estilo bloqueiam a execução do JavaScript porque o conteúdo delas é usado para construir o CSSOM. Como é possível que o JavaScript carregado precise acessar o CSSOM, a execução precisa ser adiada até que o CSSOM seja totalmente construído.

O problema é que o navegador pode construir o CSSOM sem baixar a fonte. Isso significa que, se você adicionar uma marca usando uma tag de script in-line ao DOM imediatamente após a tag <link> da folha de estilo da fonte, é provável que a marca ocorra antes de a fonte ser totalmente carregada.

Até que os eventos de carregamento de fontes estejam disponíveis nos navegadores, o JavaScript é necessário para determinar quando uma fonte está realmente ativa e pronta para uso na página. Felizmente, o carregamento de fontes via JavaScript também melhora o desempenho, já que não exige uma solicitação de bloqueio adicional a um arquivo CSS.

A maioria das fontes da Web (incluindo as do Google, do Typekit e do font.com) pode ser carregada pelo script webfont.js, que foi desenvolvido em conjunto pelo Google e pelo Typekit.

Para atualizar o documento principal e usar webfont.js para carregar as fontes (em vez da tag <link>), substitua a seção de fontes do código pelo seguinte:

<!-- Start fonts -->
<script>
window.WebFontConfig = {
  google: {families: ['Roboto:400,700,400italic']},
  timeout: 10000,
  active: function() {
    performance.mark('fonts:active');
  }
};
</script>
<script async src="https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js"></script>
<!-- End fonts -->

Há duas coisas importantes a serem observadas no código acima:

  • Ele cria uma marca "fonts:active" no callback ativo para que você possa medir depois quanto tempo levou para as fontes serem carregadas.
  • A tag <script> que carrega o webfonts.js contém o atributo async. Portanto, ela não bloqueia a análise ou a renderização do restante do documento, o que não acontece com as tags <link>.

Embora os códigos acima criem uma marca "fonts:active", medir e registrar essa marca no console não é tão simples quanto para a marca "css:unblock". O motivo é que o carregamento de fontes agora acontece de forma assíncrona. Portanto, se você tentar medir a marca "fonts:active" no manipulador window.onload (como fez com "css:unblock"), é muito provável que a fonte ainda não esteja carregada.

Para resolver esse problema, crie uma promessa que seja resolvida quando a fonte for carregada. A função a seguir faz isso por você. Copie e cole no arquivo perf-analytics.js:

/**
 * Creates a promise that is resolved once the web fonts are fully load or
 * is reject if the fonts fail to load. The resolved callback calculates the
 * time duration between the responseEnd timing event and when the web fonts
 * are downloaded and active. If an error occurs loading the font, this fact
 * is logged to the console.
 */
function measureWebfontPerfAndFailures() {
  new Promise(function(resolve, reject) {
    // The classes `wf-active` or `wf-inactive` are added to the <html>
    // element once the fonts are loaded (or error).
    var loaded = /wf-(in)?active/.exec(document.documentElement.className);
    var success = loaded && !loaded[1]; // No "in" in the capture group.
    // If the fonts are already done loading, resolve immediately.
    // Otherwise resolve/reject in the active/inactive callbacks, respectively.
    if (loaded) {
      success ? resolve() : reject();
    }
    else {
      var originalAciveCallback = WebFontConfig.active;
      WebFontConfig.inactive = reject;
      WebFontConfig.active = function() {
        originalAciveCallback();
        resolve();
      };
      // In case the webfont.js script fails to load, always reject the
      // promise after the timeout amount.
      setTimeout(reject, WebFontConfig.timeout);
    }
  })
  .then(function() {
    console.log('Fonts', 'active', measureDuration('fonts:active'));
  })
  .catch(function() {
    console.error('Error loading web fonts')
  });
}

Atualize também o gerenciador window.onload para chamar essa nova função.

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

Depois de concluir esta etapa, seu código vai corresponder ao que está no diretório 02-fonts do repositório do codelab.

Se você carregar a página em um navegador e abrir o console do desenvolvedor, verá algo como a saída a seguir:

Screen Shot 2016-05-17 at 11.13.22 AM.png

Saber quando uma imagem está visível não é tão simples quanto parece. Você sabe das etapas anteriores que as folhas de estilo e as tags <script> síncronas podem bloquear a renderização, a análise e a execução de scripts. O que você talvez não saiba é que nenhum deles bloqueia o scanner de pré-carregamento do navegador.

Um scanner de pré-carregamento é algo que todos os navegadores modernos implementam como uma das muitas tentativas de melhorar o desempenho, mesmo em sites que não são focados em desempenho e contêm muitos recursos de bloqueio. A ideia é que, embora alguns recursos possam bloquear a análise, a renderização ou a execução de scripts, eles não precisam bloquear os downloads. Assim, o navegador verifica o arquivo HTML antes de começar a construir o DOM e procura recursos que podem ser baixados imediatamente.

No caso das imagens, isso significa que há uma boa chance de elas já terem sido baixadas quando são adicionadas ao DOM. Isso também significa que o ponto em que uma imagem é baixada não é necessariamente uma boa métrica de performance. A métrica de desempenho mais importante é o ponto em que uma imagem fica visível para o usuário.

Quando uma imagem é baixada antes de ser adicionada ao DOM, o ponto em que ela fica visível é o ponto em que ela está no DOM. Por outro lado, se uma imagem não for baixada antes de ser adicionada ao DOM, ela vai ficar visível quando o manipulador onload for acionado.

Portanto, para saber quando uma imagem está visível, é necessário processar os dois casos.

Para isso, adicione marcas no manipulador onload de cada imagem e em uma tag <script> inline imediatamente após a última imagem no DOM. A ideia é que a marca que ocorre por último seja a que representa quando todas as imagens estão visíveis.

Para adicionar marcas quando as imagens são carregadas e renderizadas, atualize o código das imagens em index.html da seguinte forma:

<!-- Start images -->
<div class="gallery">
  <img onload="performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/1/">
  <img onload="performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/2/">
  <img onload="performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/3/">
</div>
<script>performance.mark('img:visible')</script>
<!-- End images -->

Como o método performance.measure para um nome de marcador específico sempre usa o último marcador (se encontrar vários marcadores com o mesmo nome), a função utilitária measureDuration no arquivo perf-analytics.js pode ser usada para isso sem modificações adicionais:

/**
 * Calculates the time duration between the responseEnd timing event and when
 * all images are loaded and visible on the page, then logs that value to the
 * console.
 */
function measureImagesVisibleTime() {
  console.log('Images', 'visible', measureDuration('img:visible'));
}

Adicione a função acima ao arquivo perf-analytics.js e atualize o manipulador window.onload para chamá-la:

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

Depois de concluir esta etapa, seu código vai corresponder ao que está no diretório 03-images do repositório do codelab.

Se você carregar a página em um navegador e abrir o console do desenvolvedor, verá algo como a saída a seguir:

Screen Shot 2016-05-17 at 11.13.39 AM.png

Como as tags <script> sem o atributo async bloqueiam a análise do DOM até serem baixadas e executadas, é possível determinar o ponto em que todos os scripts terminaram de ser executados adicionando uma marca em uma tag de script inline imediatamente após o último <script> síncrono no DOM.

O uso de manipuladores onload não funciona nesse caso, já que o navegador precisa executar o script depois de carregá-lo, o que leva tempo. Um script que carrega rápido, mas é lento para executar, pode ser tão ruim quanto um script de carregamento lento.

Para rastrear quando todo o JavaScript é carregado e executado no documento principal, atualize a seção JavaScript em index.html com o seguinte código:

<!-- Start JavaScript -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script>performance.mark('js:execute');</script>
<!-- End JavaScript -->

Isso adiciona uma marca chamada "js:execute" imediatamente após o download e a execução dos scripts para plug-ins do jQuery e do Bootstrap.

Para medir quanto tempo isso leva, adicione a seguinte função a perf-analytics.js:

/**
 * Calculates the time duration between the responseEnd timing event and when
 * all synchronous JavaScript files have been downloaded and executed, then
 * logs that value to the console.
 */
function measureJavaSciptExecutionTime() {
  console.log('JavaScript', 'execute', measureDuration('js:execute'));
}

Em seguida, invoque-o no manipulador window.onload:

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

Depois de concluir esta etapa, seu código vai corresponder ao que está no diretório 04-javascript do repositório do codelab.

Se você carregar a página em um navegador e abrir o console do desenvolvedor, verá algo como a saída a seguir:

Screen Shot 2016-05-17 at 11.14.03 AM.png

Nem todos os navegadores são compatíveis com promessas JavaScript ou APIs User Timing. Se você executar o código escrito até agora em um navegador sem suporte para um desses recursos, vai receber erros.

Para lidar com isso, use a detecção de recursos. Adicione o seguinte trecho de código imediatamente antes da seção de fontes. Essa linha de JavaScript detecta o suporte ao método performance.mark. Portanto, ela precisa ser adicionada à página antes que esse método seja usado:

<!-- Start feature detects -->
<script>window.__perf = window.performance && performance.mark;</script>
<!-- End feature detects -->

Em seguida, em qualquer lugar do index.html em que você chama performance.mark, adicione o prefixo com a detecção de recursos. Confira um exemplo que atualiza o código no bloco de imagem, mas não se esqueça de atualizar as outras seções também.

<!-- Start images -->
<div class="gallery">
  <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/1/">
  <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/2/">
  <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/3/">
</div>
<script>__perf && performance.mark('img:visible')</script>
<!-- End images -->

Agora que a variável __perf está definida em window, você também pode usá-la em perf-analytics.js para não chamar um método que não é compatível com o navegador atual.

A função measureDuration precisa ser atualizada para adicionar a seguinte condição:

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

Por fim, como nem todos os navegadores são compatíveis com promessas do JavaScript, a função measureWebfontPerfAndFailures também precisa ser envolvida em uma condição:

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

Agora você pode executar o código em qualquer navegador sem problemas.

Depois de concluir esta etapa, seu código vai corresponder ao que está no diretório 05-feature-detects do repositório do codelab.

A etapa final deste codelab é pegar os dados que estão sendo registrados no console e enviá-los ao Google Analytics. Antes de enviar dados ao Google Analytics, adicione a biblioteca analytics.js e o snippet de acompanhamento padrão à sua página.

Adicione o código a seguir ao index.html após o bloco principal de JavaScript, mas antes do carregamento do script perf-analytics.js:

<!-- Start analytics tracking snippet -->
<script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');
</script>
<script async src="https://www.google-analytics.com/analytics_debug.js"></script>
<!-- End analytics tracking snippet -->

Se você já adicionou o Google Analytics a um site, sabe que é necessário substituir o marcador de posição "UA-XXXXX-Y" pelo ID de acompanhamento recebido ao criar uma propriedade no Google Analytics.

O snippet de acompanhamento da analytics.js faz quatro coisas principais:

  • Cria um elemento <script> assíncrono que faz o download da biblioteca JavaScript analytics.js.
  • Inicializa uma função ga() global (chamada de fila de comandos ga()) que permite programar comandos para serem executados depois que a biblioteca analytics.js for carregada e estiver pronta para ser usada.
  • Adiciona um comando à fila de comandos do ga() com o objetivo de criar um novo objeto do rastreador para a propriedade especificada pelo parâmetro "UA-XXXXX-Y".
  • Inclui outro comando na fila de comandos do ga() para enviar uma visualização de página atual ao Google Analytics.

Embora os dados coletados apenas com visualizações de página sejam úteis, eles não contam toda a história. Para ter uma ideia melhor de como os usuários estão interagindo com seu site ou aplicativo, envie mais dados de interação ao Google Analytics.

O Google Analytics é compatível com vários tipos de dados de interação: visualizações de página, eventos, interações sociais, exceções e (por último, mas não menos importante) tempos do usuário. Para enviar dados de velocidade do usuário ao Google Analytics, use a seguinte assinatura de comando:

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

Em que timingCategory é uma string que permite organizar hits de tempo em um grupo lógico, timingVar é a variável que você está medindo e timingValue é a duração real em milissegundos.

Para ver como isso funciona na prática, a instrução console.log na função measureCssUnblockTime pode ser atualizada da seguinte maneira:

ga('send', 'timing', 'CSS', 'unblock', measureDuration('css:unblock'));

Embora o código acima funcione em algumas situações, há duas armadilhas importantes que você precisa conhecer:

  • A etapa anterior atualizou a função measureDuration para ser executada apenas se o navegador for compatível com a API User Timings, o que significa que, às vezes, ela vai retornar undefined. Como não há motivo para enviar dados indefinidos ao Google Analytics (em alguns casos, isso pode até prejudicar seus relatórios), envie esse hit de tempo apenas se measureDuration retornar um valor.
  • Quando measureDuration retorna um valor, ele é um DOMHighResTimeStamp, que tem precisão maior que milissegundos. Como timingValue no Google Analytics precisa ser um número inteiro, você precisa arredondar o valor retornado por measureDuration.

Para evitar esses problemas, atualize a instrução de retorno na função "measureDuration" para arredondar o valor de retorno:

function measureDuration(mark, opt_reference) {
  if (window.__perf) {
    // ...
    return Math.round(measure.duration);
  }
}

Atualize os comandos de tempo para serem executados apenas se houver um valor para a métrica em questão. Por exemplo, a função measureCssUnblockTime precisa ser atualizada para algo assim:

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

Você precisará fazer atualizações semelhantes em todas as outras funções de medição. Depois de concluído, o arquivo final perf-analytics.js vai ficar assim:

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


/**
 * Calculates the time duration between the responseEnd timing event and when
 * the CSS stops blocking rendering, then sends this measurement to Google
 * Analytics via a timing hit.
 */
function measureCssUnblockTime() {
  var cssUnblockTime = measureDuration('css:unblock');
  if (cssUnblockTime) {
    ga('send', 'timing', 'CSS', 'unblock', cssUnblockTime);
  }
}


/**
 * Calculates the time duration between the responseEnd timing event and when
 * the web fonts are downloaded and active, then sends this measurement to
 * Google Analytics via a timing hit. If an error occurs loading the font, an
 * error event is sent to Google Analytics.
 */
function measureWebfontPerfAndFailures() {
  if (window.Promise) {
    new Promise(function(resolve, reject) {
      var loaded = /wf-(in)?active/.exec(document.documentElement.className);
      var success = loaded && !loaded[1]; // No "in" in the capture group.
      if (loaded) {
        success ? resolve() : reject();
      }
      else {
        var originalAciveCallback = WebFontConfig.active;
        WebFontConfig.inactive = reject;
        WebFontConfig.active = function() {
          originalAciveCallback();
          resolve();
        };
        // In case the webfont.js script failed to load.
        setTimeout(reject, WebFontConfig.timeout);
      }
    })
    .then(function() {
      var fontsActiveTime = measureDuration('fonts:active');
      if (fontsActiveTime) {
        ga('send', 'timing', 'Fonts', 'active', fontsActiveTime);
      }
    })
    .catch(function() {
      ga('send', 'event', 'Fonts', 'error');
    });
  }
}


/**
 * Calculates the time duration between the responseEnd timing event and when
 * all images are loaded and visible on the page, then sends this measurement
 * to Google Analytics via a timing hit.
 */
function measureImagesVisibleTime() {
  var imgVisibleTime = measureDuration('img:visible');
  if (imgVisibleTime) {
    ga('send', 'timing', 'Images', 'visible', imgVisibleTime);
  }
}


/**
 * Calculates the time duration between the responseEnd timing event and when
 * all synchronous JavaScript files are downloaded and executed, then sends
 * this measurement to Google Analytics via a timing hit.
 */
function measureJavaSciptExecutionTime() {
  var jsExecuteTime = measureDuration('js:execute');
  if (jsExecuteTime) {
    ga('send', 'timing', 'JavaScript', 'execute', jsExecuteTime);
  }
}


/**
 * Accepts a mark name and an optional reference point in the navigation timing
 * API and returns the time duration between the reference point and the last
 * mark (chronologically). The return value is rounded to the nearest whole
 * number to be compatible with Google Analytics.
 * @param {string} mark The mark name.
 * @param {string=} opt_reference An optional reference point from the
 *     navigation timing API. Defaults to 'responseEnd'.
 * @return {?number} The time duration as an integer or undefined if no
 *     matching marks can be found.
 */
function measureDuration(mark, opt_reference) {
  if (window.__perf) {
    var reference = opt_reference || 'responseEnd';
    var name = reference + ':' + mark;

    // Clears any existing measurements with the same name.
    performance.clearMeasures(name);

    // Creates a new measurement from the reference point to the specified mark.
    // If more than one mark with this name exists, the most recent one is used.
    performance.measure(name, reference, mark);

    // Gets the value of the measurement just created.
    var measure = performance.getEntriesByName(name)[0];

    // Returns the measure duration.
    return Math.round(measure.duration);
  }
}

O arquivo index.html final vai ficar assim:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Performance Analytics Demo</title>

  <!-- Start navigation timing feature detect -->
  <script>window.__perf = window.performance && performance.mark;</script>
  <!-- End navigation timing feature detect -->

  <!-- Start fonts -->
  <script>
  window.WebFontConfig = {
    google: {families: ['Roboto:400,700,400italic']},
    timeout: 10000,
    active: function() {
      __perf && performance.mark('fonts:active');
    }
  };
  </script>
  <script async src="https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js"></script>
  <!-- End fonts -->

  <!-- Start CSS -->
  <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
  <style>
    body { font-family: Roboto, sans-serif; margin: 1em; }
    img { float: left; height: auto; width: 33.33%; }
    .gallery { overflow: hidden; }
  </style>
  <script>__perf && performance.mark('css:unblock');</script>
  <!-- End CSS -->

</head>
<body>

  <div class="container">

    <!-- Start images -->
    <div class="gallery">
      <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/1/">
      <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/2/">
      <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/3/">
    </div>
    <script>__perf && performance.mark('img:visible')</script>
    <!-- End images -->

    <h1>Performance Analytics Demo</h1>
    <p>Real performance data from real users.</p>

  </div>

  <!-- Start JavaScript -->
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
  <script>__perf && performance.mark('js:execute');</script>
  <!-- End JavaScript -->

  <!-- Start analytics tracking snippet -->
  <script>
  window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
  ga('create', 'UA-XXXXX-Y', 'auto');
  ga('send', 'pageview');
  </script>
  <script async src="https://www.google-analytics.com/analytics.js"></script>
  <!-- End analytics tracking snippet -->

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

</body>
</html>

Se você carregar esta página e analisar as solicitações no painel de rede, verá algo como o seguinte:

Screen Shot 2016-05-10 at 6.57.23 PM.png

Isso é útil, mas pode ser complicado analisar esses dados como uma solicitação codificada por URL. E, se por algum motivo você não vir essas solicitações, será muito difícil rastrear onde ocorreu a falha.

Uma abordagem melhor ao desenvolver localmente é usar a versão de depuração da analytics.js, que registra informações úteis de depuração no console à medida que cada comando da analytics.js é executado. Se você

atualize o URL analytics.js em index.html para analytics_debug.js e abra o console do navegador. Você vai ver instruções impressas parecidas com esta:

Screen Shot 2016-05-10 at 6.54.13 PM.png

Agora que você sabe como implementar a medição de performance para esta página de demonstração, tente adicionar ao seu site, enviando dados reais de usuários para o Google Analytics.

Gerar relatórios sobre os dados coletados

Depois de coletar dados de performance por alguns dias, você poderá gerar relatórios com essas informações para ter insights úteis sobre a velocidade de carregamento do seu site e dos recursos dele para usuários reais.

Para acessar os relatórios de tempo do usuário no Google Analytics, clique na guia "Relatórios" na parte de cima e selecione "Comportamento > Velocidade do site > Tempos do usuário" na navegação da barra lateral. Você também pode seguir as instruções para acessar o relatório Tempos do usuário na Central de Ajuda.

Ao carregar o relatório de tempos do usuário no Google Analytics, você poderá ver as categorias de tempo que correspondem aos dados enviados. Clique em qualquer um deles para ver visualizações detalhadas dos seus dados de tempo. A imagem a seguir é um exemplo dos tempos de carregamento de fontes em um site real usando o Google Fonts nas últimas 24 horas.

Screen Shot 2016-05-10 at 7.16.07 PM.png

Parabéns! Você concluiu este laboratório de programação. Se quiser se aprofundar, a próxima seção vai dar algumas sugestões de como criar com base nesse código para ter ainda mais insights.

As métricas de desempenho abordadas neste codelab são essenciais para medir como seu site é carregado para usuários reais, mas são apenas o começo. Se quiser se aprofundar na análise de performance, uma próxima etapa fácil é acompanhar mais métricas.

Neste codelab, você rastreou métricas relacionadas a quando os recursos estavam disponíveis para o usuário. Se quiser, você pode detalhar ainda mais a maioria deles. Por exemplo, em vez de medir apenas quando o JavaScript terminou de ser executado, você pode medir quando ele começou a carregar, quando terminou de carregar, quando começou a ser executado e, finalmente, quando terminou de ser executado. Cada uma dessas métricas pode revelar um problema que apenas uma delas não mostraria.

Além de mais detalhes, você também precisa pensar de forma mais abrangente na sua estratégia geral de análise de performance. Quais são as metas? O que é sucesso?

Quando se trata de qualquer tipo de análise, geralmente é bom começar com uma pergunta e descobrir como usar os dados para respondê-la.

Por exemplo, considere a lista de perguntas a seguir e como você usaria o conhecimento adquirido neste codelab para usar a análise de dados e responder a elas:

  • Os valores das métricas que você está rastreando estão diminuindo ou aumentando com o tempo?
  • Como o uso do cache off-line via service worker ou armazenamento local afetaria o desempenho geral do seu site?
  • Seus recursos estão sendo carregados de maneira ideal? Por exemplo, há uma grande diferença entre o momento em que o recurso é baixado e quando ele está disponível para uso?
  • Há alguma correlação entre a performance e outras métricas que você está acompanhando (por exemplo, taxa de inscrição, tempo no site, compras etc.)?

Por fim, se quiser saber mais sobre performance na Web ou Google Analytics, confira estes recursos para começar: