Otimizar o JavaScript de terceiros

Os scripts de terceiros afetam a performance, por isso é importante fazer auditorias regularmente e usar técnicas eficientes de carregamento. Este codelab mostra como otimizar o carregamento de recursos de terceiros. Ele aborda as seguintes técnicas:

  • Como adiar o carregamento do script

  • Carregar lentamente recursos não críticos

  • Pré-conexão com origens obrigatórias

O app de exemplo inclui uma página da Web simples com três recursos de fontes de terceiros:

  • Uma incorporação de vídeo

  • Uma biblioteca de visualização de dados para renderizar um gráfico de linhas

  • Um widget de compartilhamento de mídias sociais

Captura de tela da página com os recursos de terceiros destacados.
Recursos de terceiros no app de exemplo.

Você vai começar medindo o desempenho do app e depois aplicar cada técnica para melhorar diferentes aspectos do desempenho do app.

Avaliar o desempenho

Primeiro, abra o app de exemplo na visualização em tela cheia:

  1. Clique em Remix to Edit para tornar o projeto editável.
  2. Para visualizar o site, pressione View App. Em seguida, pressione Fullscreen tela cheia.

Execute uma auditoria de desempenho do Lighthouse na página para estabelecer a performance de referência:

  1. Pressione "Control+Shift+J" (ou "Command+Option+J" no Mac) para abrir as Ferramentas do desenvolvedor.
  2. Clique na guia Lighthouse.
  3. Clique em Dispositivos móveis.
  4. Marque a caixa de seleção Performance. Você pode limpar o restante das caixas de seleção na seção "Audits".
  5. Clique em Simulação de 3G rápido, lentidão da CPU 4x.
  6. Marque a caixa de seleção Limpar armazenamento.
  7. Clique em Executar auditorias.

Quando você executa uma auditoria na sua máquina, os resultados exatos podem variar, mas o tempo da First Contentful Paint (FCP) é muito alto, e o Lighthouse sugere duas oportunidades para investigar: Eliminar recursos que bloqueiam a renderização e Conectar antecipadamente às origens necessárias. Mesmo que todas as métricas estejam em verde, as otimizações ainda vão gerar melhorias.

Captura de tela da auditoria do Lighthouse mostrando 2,4 segundos de FCP e duas oportunidades: eliminar recursos que bloqueiam a renderização e pré-conectar às origens necessárias.

Adiar o JavaScript de terceiros

A auditoria Eliminar recursos de bloqueio de renderização identificou que você pode economizar tempo adiando um script proveniente de d3js.org:

Captura de tela da auditoria "Eliminar recursos que impedem a renderização" com o script d3.v3.min.js destacado.

A D3.js é uma biblioteca JavaScript para criar visualizações de dados. O arquivo script.js no app de exemplo usa funções de utilitário D3 para criar o gráfico de linhas SVG e anexá-lo à página. A ordem das operações é importante: script.js precisa ser executado depois que o documento for analisado e a biblioteca D3 for carregada. É por isso que ele é incluído logo antes da tag </body> de fechamento em index.html.

No entanto, o script D3 é incluído no <head> da página, o que bloqueia a análise do restante do documento:

Captura de tela do index.html com a tag de script destacada no cabeçalho.

Dois atributos mágicos podem desbloquear o analisador quando adicionados à tag de script:

  • async garante que os scripts sejam transferidos em segundo plano e executados na primeira oportunidade após o término do download.

  • defer garante que os scripts sejam transferidos por download em segundo plano e executados depois que a análise for concluída.

Como esse gráfico não é muito importante para a página em geral e provavelmente estará abaixo da dobra, use defer para garantir que não haja bloqueio de parser.

Etapa 1: carregar o script de forma assíncrona com o atributo defer

Na linha 17 de index.html, adicione o atributo defer ao elemento <script>:

<script src="https://d3js.org/d3.v3.min.js" defer></script>

Etapa 2: verificar se a ordem das operações está correta

Agora que o D3 está adiado, o script.js será executado antes que o D3 esteja pronto, resultando em um erro.

Os scripts com o atributo defer são executados na ordem em que foram especificados. Para garantir que script.js seja executado depois que o D3 estiver pronto, adicione defer a ele e mova-o para a <head> do documento, logo após o elemento <script> do D3. Agora ele não bloqueia mais o analisador, e o download começa mais cedo.

<script src="https://d3js.org/d3.v3.min.js" defer></script>
<script src="./script.js" defer></script>

Recursos de terceiros com carregamento lento

Todos os recursos abaixo da dobra são bons candidatos para o carregamento lento.

O app de exemplo tem um vídeo do YouTube incorporado em um iframe. Para conferir quantas solicitações a página faz e quais delas vêm do iframe do YouTube incorporado:

  1. Para visualizar o site, pressione View App. Em seguida, pressione Fullscreen tela cheia.
  2. Pressione "Control+Shift+J" (ou "Command+Option+J" no Mac) para abrir as Ferramentas do desenvolvedor.
  3. Clique na guia Rede.
  4. Marque a caixa de seleção Desativar cache.
  5. Selecione 3G rápido no menu suspenso Limitação de taxa.
  6. Recarregue a página.

Captura de tela do painel de rede do DevTools.

O painel Rede revela que a página fez um total de 28 solicitações e transferiu quase 1 MB de recursos compactados.

Para identificar as solicitações feitas pelo iframe do YouTube, procure o ID do vídeo 6lfaiXM6waw na coluna Iniciador. Para agrupar todas as solicitações por domínio:

  • No painel Rede, clique com o botão direito do mouse em um título de coluna.

  • No menu suspenso, selecione a coluna Domínios.

  • Para classificar as solicitações por domínio, clique no título da coluna Domínios.

A nova classificação revela que há outras solicitações para domínios do Google. No total, o iframe do YouTube faz 14 solicitações de scripts, folhas de estilo, imagens e fontes. No entanto, a menos que os usuários rolem a tela para baixo para reproduzir o vídeo, eles não precisam de todos esses recursos.

Ao esperar para carregar o vídeo de forma lenta até que o usuário role para baixo até essa seção da página, você reduz o número de solicitações que a página faz inicialmente. Essa abordagem salva os dados dos usuários e acelera o carregamento inicial.

Uma maneira de implementar o carregamento lento é usar o Intersection Observer, uma API do navegador que notifica quando um elemento entra ou sai da viewport do navegador.

Etapa 1: impedir o carregamento inicial do vídeo

Para carregar o iframe do vídeo de maneira lazy, primeiro é necessário impedir o carregamento da maneira usual. Para fazer isso, substitua o atributo src pelo atributo data-src para especificar o URL do vídeo:

<iframe width="560" height="315" data-src="https://www.youtube.com/embed/lS9D6w1GzGY" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

data-src é um atributo de dados, que permite armazenar informações extras em elementos HTML padrão. Um atributo de dados pode ser nomeado como quiser, desde que comece com "data-".

Um iframe sem src simplesmente não será carregado.

Etapa 2: usar o Intersection Observer para carregar o vídeo de forma lazy

Para carregar o vídeo quando um usuário rola até ele, você precisa saber quando isso acontece. É aí que entra a API Intersection Observer. A API Intersection Observer permite registrar uma função de callback que é executada sempre que um elemento que você quer rastrear entra ou sai da viewport.

Para começar, crie um novo arquivo e chame-o de lazy-load.js:

  • Clique em Novo arquivo e dê um nome.
  • Clique em Adicionar este arquivo.

Adicione a tag de script ao cabeçalho do documento:

 <script src="/lazy-load.js" defer></script>

Em lazy-load.js, crie um novo IntersectionObserver e transmita uma função de callback para execução:

// create a new Intersection Observer
let observer = new IntersectionObserver(callback);

Agora, dê a observer um elemento de destino para assistir (o iframe do vídeo, neste caso) transmitindo-o como um argumento no método observe:

// the element that you want to watch
const element = document.querySelector('iframe');

// register the element with the observe method
observer.observe(element);

callback recebe uma lista de objetos IntersectionObserverEntry e o próprio objeto IntersectionObserver. Cada entrada contém um elemento target e propriedades que descrevem as dimensões, a posição, o momento em que ele entrou na viewport e muito mais. Uma das propriedades de IntersectionObserverEntry é isIntersecting, um valor booleano igual a true quando o elemento entra na viewport.

Neste exemplo, target é iframe. isIntersecting é igual a true quando target entra na janela de visualização. Para conferir isso em ação, substitua callback pela seguinte função:

let observer = new IntersectionObserver(callback);
let observer = new IntersectionObserver(function(entries, observer) {
    entries.forEach(entry => {
      console.log(entry.target);
      console.log(entry.isIntersecting);
    });
  });
  1. Para visualizar o site, pressione View App. Em seguida, pressione Fullscreen tela cheia.
  2. Pressione "Control+Shift+J" (ou "Command+Option+J" no Mac) para abrir as Ferramentas do desenvolvedor.
  3. Clique na guia Console.

Tente rolar para cima e para baixo. Você vai notar a mudança no valor de isIntersecting e o elemento de destino registrado no console.

Para carregar o vídeo quando o usuário rola até a posição, use isIntersecting como uma condição para executar uma função loadElement, que recebe o valor do data-src do elemento iframe e o define como o atributo src do elemento iframe. Essa substituição aciona o carregamento do vídeo. Em seguida, quando o vídeo for carregado, chame o método unobserve no observer para parar de assistir o elemento de destino:

let observer = new IntersectionObserver(function (entries, observer) {
  entries.forEach(entry => {
    console.log(entry.target);
    console.log(entry.isIntersecting);
  });
});
    if (entry.isIntersecting) {
      // do this when the element enters the viewport
      loadElement(entry.target);
      // stop watching
      observer.unobserve(entry.target);
    }
  });
});

function loadElement(element) {
  const src = element.getAttribute('data-src');
  element.src = src;
}

Etapa 3: reavaliar a performance

Para conferir como o tamanho e o número de recursos mudaram, abra o painel Network das ferramentas do desenvolvedor e atualize a página novamente. O painel Rede revela que a página fez 14 solicitações e apenas 260 KB. Essa é uma melhoria significativa.

Role a página para baixo e observe o painel Rede. Quando você chegar ao vídeo, a página vai acionar outras solicitações.

Pré-conectar às origens necessárias

Você adicionou o JavaScript não essencial e carregou as solicitações do YouTube de forma lazy-load. Agora é hora de otimizar o conteúdo de terceiros restante.

Adicionar o atributo rel=preconnect a um link instrui o navegador a estabelecer uma conexão com um domínio antes que a solicitação para esse recurso seja feita. Esse atributo é melhor usado em origens que fornecem recursos que você tem certeza que a página precisa.

A auditoria do Lighthouse que você executou na primeira etapa sugeriu em Preconnect to required origins que é possível economizar cerca de 400 ms estabelecendo conexões antecipadas em staticxx.facebook.com e youtube.com:

Pré-conexão com a auditoria de origem necessária com o domínio staticxx.facebook.com destacado.

Como o vídeo do YouTube agora é carregado de forma lenta, só há o staticxx.facebook.com, a origem do widget de compartilhamento de mídias sociais. Estabelecer uma conexão antecipada com esse domínio é tão simples quanto adicionar uma tag <link> ao <head> do documento:

  <link rel="preconnect" href="https://staticxx.facebook.com">

Reavaliar a performance

Confira o estado da página após a otimização. Siga as etapas da seção Medir a performance do codelab para executar outra auditoria do Lighthouse.

Auditoria do Lighthouse mostrando FCP de 1 segundo e a pontuação de desempenho de 99.