Não combater o scanner de pré-carregamento do navegador

Saiba o que é o scanner de pré-carregamento do navegador, como ele ajuda no desempenho e como você pode ficar fora do caminho.

Um aspecto negligenciado da otimização da velocidade da página envolve saber um pouco sobre os componentes internos do navegador. Os navegadores fazem certas otimizações para melhorar o desempenho de maneiras que nós, desenvolvedores, não podem, mas somente desde que essas otimizações não sejam frustradas involuntariamente.

Uma otimização interna do navegador que você precisa entender é o scanner de pré-carregamento do navegador. Neste post, vamos explicar como o scanner de pré-carregamento funciona e, mais importante, como você pode evitar que ele atrapalhe.

O que é um scanner de pré-carregamento?

Todos os navegadores têm um analisador de HTML principal que tokeniza a marcação bruta e a processa em um modelo de objeto. Tudo isso continua até que o analisador faz uma pausa quando encontra um recurso de bloqueio, como uma folha de estilo carregada com um elemento <link> ou um script carregado com um elemento <script> sem um atributo async ou defer.

Diagrama do analisador de HTML.
Fig. 1:um diagrama de como o analisador HTML principal do navegador pode ser bloqueado. Nesse caso, o analisador encontra um elemento <link> para um arquivo CSS externo, que impede o navegador de analisar o restante do documento (ou mesmo renderizar qualquer parte dele) até que o CSS seja baixado e analisado.

No caso de arquivos CSS, a renderização é bloqueada para evitar um flash de conteúdo sem estilo (FOUC, na sigla em inglês), que é quando uma versão sem estilo de uma página pode ser vista brevemente antes que os estilos sejam aplicados a ela.

A página inicial do web.dev em um estado sem estilo (à esquerda) e em um estado estilizado (à direita).
Figura 2. Um exemplo simulado de FOUC. À esquerda, está a página inicial do web.dev sem estilos. À direita, está a mesma página com os estilos aplicados. O estado sem estilo pode ocorrer em um flash se o navegador não bloquear a renderização enquanto uma folha de estilo está sendo baixada e processada.

O navegador também bloqueia a análise e a renderização da página quando encontra elementos <script> sem um atributo defer ou async.

A razão para isso é que o navegador não pode saber com certeza se algum script modificará o DOM enquanto o analisador HTML primário ainda está fazendo seu trabalho. Por isso, é uma prática comum carregar o JavaScript no final do documento para que os efeitos da análise e renderização bloqueadas se tornem marginais.

Esses são bons motivos para o navegador precisar bloquear a análise e a renderização. No entanto, bloquear qualquer uma dessas etapas importantes é indesejável, porque pode atrasar a descoberta de outros recursos importantes. Felizmente, os navegadores fazem o possível para reduzir esses problemas usando um analisador de HTML secundário chamado verificador de pré-carregamento.

Diagrama do analisador de HTML principal (à esquerda) e do scanner de pré-carregamento (à direita), que é o analisador de HTML secundário.
Figura 3: diagrama que mostra como o scanner de pré-carregamento funciona em paralelo com o analisador HTML principal para carregar recursos especulativamente. Aqui, o analisador HTML primário é bloqueado ao carregar e processar CSS antes de começar a processar a marcação de imagem no elemento <body>. No entanto, o scanner de pré-carregamento pode procurar esse recurso de imagem e começar a carregá-lo antes que o analisador HTML principal seja desbloqueado.

A função de um verificador de pré-carregamento é especulativa, ou seja, ele examina a marcação bruta para encontrar recursos a serem buscados oportunamente antes que o analisador HTML principal os descubra.

Como saber quando o scanner de pré-carregamento está funcionando

O scanner de pré-carregamento existe devido ao bloqueio da renderização e da análise. Se esses dois problemas de desempenho nunca tivessem existido, o scanner de pré-carregamento não seria muito útil. A chave para descobrir se uma página da Web se beneficia do scanner de pré-carregamento depende desses fenômenos de bloqueio. Para isso, você pode introduzir um atraso artificial nas solicitações para descobrir onde o scanner de pré-carregamento está funcionando.

Considere esta página de texto e imagens básicos com uma folha de estilo como exemplo. Como os arquivos CSS bloqueiam a renderização e a análise, você introduz um atraso artificial de dois segundos na folha de estilo por meio de um serviço de proxy. Esse atraso facilita a visualização na hierarquia da rede em que o verificador de pré-carregamento está funcionando.

O gráfico de cascata de rede do WebPageTest ilustra um atraso artificial de dois segundos imposto à folha de estilo.
Figura 4. Gráfico em cascata de rede do WebPageTest de uma página da Web executada no Chrome em um dispositivo móvel em uma conexão 3G simulada. Mesmo que a folha de estilo seja artificialmente atrasada por um proxy dois segundos antes de começar a carregar, a imagem localizada mais tarde no payload de marcação é descoberta pelo scanner de pré-carregamento.

Como você pode ver na hierarquia, o scanner de pré-carregamento descobre o elemento <img> mesmo com a renderização e a análise de documentos bloqueadas. Sem essa otimização, o navegador não pode buscar coisas de forma oportunista durante o período de bloqueio, e mais solicitações de recursos seriam consecutivas em vez de simultâneas.

Com esse exemplo de brinquedo pronto, vamos dar uma olhada em alguns padrões do mundo real em que o scanner de pré-carregamento pode ser derrotado e o que pode ser feito para corrigi-los.

Scripts async injetados

Digamos que você tenha um HTML no <head> que inclua um JavaScript in-line como este:

<script>
  const scriptEl = document.createElement('script');
  scriptEl.src = '/yall.min.js';

  document.head.appendChild(scriptEl);
</script>

Por padrão, os scripts injetados são async. Portanto, quando esse script é injetado, ele se comporta como se o atributo async fosse aplicado a ele. Isso significa que ele será executado o mais rápido possível e não bloqueará a renderização. Parece ideal, certo? No entanto, se você presumir que esse <script> inline vem depois de um elemento <link> que carrega um arquivo CSS externo, o resultado não será ideal:

Este gráfico do WebPageTest mostra a verificação de pré-carregamento derrotada quando um script é injetado.
Figura 5. Diagrama em cascata de rede do WebPageTest de uma página da Web executada no Chrome em um dispositivo móvel em uma conexão 3G simulada. A página contém uma única folha de estilo e um script async injetado. O scanner de pré-carregamento não consegue descobrir o script durante a fase de bloqueio de renderização porque ele é injetado no cliente.

Vamos detalhar o que aconteceu aqui:

  1. Em 0 segundo, o documento principal é solicitado.
  2. Em 1,4 segundo, chega o primeiro byte da solicitação de navegação.
  3. Após 2 segundos, o CSS e a imagem são solicitados.
  4. Como o parser está bloqueado para carregar a folha de estilo, e o JavaScript inline que injeta o script async aparece depois dessa folha de estilo em 2,6 segundos, a funcionalidade fornecida pelo script não está disponível assim que poderia.

Isso não é ideal porque a solicitação do script ocorre somente após o término do download da folha de estilo. Isso atrasa a execução do script o mais rápido possível. Em contrapartida, como o elemento <img> é detectável na marcação fornecida pelo servidor, ele é descoberto pelo scanner de pré-carregamento.

O que acontece se você usar uma tag <script> comum com o atributo async em vez de injetar o script no DOM?

<script src="/yall.min.js" async></script>

Este é o resultado:

Uma hierarquia de rede do WebPageTest que mostra como um script assíncrono carregado usando o elemento de script HTML ainda é detectável pelo verificador de pré-carregamento do navegador, mesmo que o analisador HTML principal do navegador esteja bloqueado durante o download e o processamento de uma folha de estilo.
Fig. 6:um gráfico de cascata de rede do WebPageTest de uma página da Web, executado no Chrome em um dispositivo móvel com uma conexão 3G simulada. A página contém uma única folha de estilo e um único elemento async <script>. O verificador de pré-carregamento descobre o script durante a fase de bloqueio de renderização e o carrega simultaneamente com o CSS.

Pode ser tentador sugerir que esses problemas podem ser corrigidos usando rel=preload. Isso certamente funciona, mas pode ter alguns efeitos colaterais. Afinal, por que usar rel=preload para corrigir um problema que pode ser evitado não injetando um elemento <script> no DOM?

Uma hierarquia do WebPageTest mostrando como a sugestão de recurso rel=preload é usada para promover a descoberta de um script injetado assíncrono, embora de uma forma que possa ter efeitos colaterais não intencionais.
Figura 7. Diagrama em cascata de rede do WebPageTest de uma página da Web executada no Chrome em um dispositivo móvel em uma conexão 3G simulada. A página contém uma única folha de estilo e um script async injetado, mas o script async é carregado previamente para garantir que ele seja descoberto mais cedo.

O pré-carregamento "corrige" o problema aqui, mas introduz um novo problema: o script async nas duas primeiras demonstrações, apesar de ser carregado no <head>, é carregado com prioridade "Baixa", enquanto a folha de estilo é carregada com prioridade "Mais alta". Na última demonstração, em que o script async é carregado previamente, a folha de estilos ainda é carregada com a prioridade "Mais alta", mas a prioridade do script foi promovida para "Alta".

Quando a prioridade de um recurso é elevada, o navegador aloca mais largura de banda para ele. Isso significa que, embora a folha de estilo tenha a prioridade mais alta, a prioridade elevada do script pode causar contenção da largura de banda. Isso pode ser um fator em conexões lentas ou em casos em que os recursos são muito grandes.

A resposta é simples: se um script for necessário durante a inicialização, não injete-o no DOM para burlar o scanner de pré-carregamento. Faça experimentos conforme necessário com a colocação de elementos <script>, bem como com atributos como defer e async.

Carregamento lento com JavaScript

O carregamento lento é um ótimo método de conservação de dados e é geralmente aplicado a imagens. No entanto, às vezes, o carregamento lento é aplicado incorretamente a imagens que estão "acima da dobra", por assim dizer.

Isso pode causar problemas de descoberta de recursos relacionados ao scanner de pré-carregamento e atrasar desnecessariamente o tempo necessário para descobrir uma referência a uma imagem, fazer o download dela, decodificá-la e apresentá-la. Vamos usar este exemplo de marcação de imagem:

<img data-src="/sand-wasp.jpg" alt="Sand Wasp" width="384" height="255">

O uso de um prefixo data- é um padrão comum em carregadores lentos com tecnologia JavaScript. Quando a imagem é rolada para a viewport, o carregador lento remove o prefixo data-. Isso significa que, no exemplo anterior, data-src se torna src. Essa atualização solicita que o navegador busque o recurso.

Esse padrão não é problemático até ser aplicado a imagens que estão na viewport durante a inicialização. Como o scanner de pré-carregamento não lê o atributo data-src da mesma forma que um atributo src (ou srcset), a referência de imagem não é descoberta antes. Pior ainda, o carregamento da imagem é atrasado até depois que o JavaScript do carregador lento faz o download, compila e executa.

Um gráfico de cascata de rede do WebPageTest mostrando como uma imagem carregada de forma lenta que está na viewport durante a inicialização é necessariamente atrasada porque o scanner de pré-carregamento do navegador não consegue encontrar o recurso de imagem e só carrega quando o JavaScript necessário para o carregamento lento é usado. A imagem é descoberta muito mais tarde do que deveria.
Figura 8. Diagrama em cascata de rede do WebPageTest de uma página da Web executada no Chrome em um dispositivo móvel em uma conexão 3G simulada. O recurso de imagem é carregado de forma desnecessária, mesmo que esteja visível na viewport durante a inicialização. Isso derrota o scanner de pré-carregamento e causa um atraso desnecessário.

Dependendo do tamanho da imagem, que pode depender do tamanho da janela de visualização, ela pode ser um elemento candidato para a maior exibição de conteúdo (LCP). Quando o scanner de pré-carregamento não consegue buscar o recurso de imagem de forma especulativa com antecedência, possivelmente durante o ponto em que as folhas de estilo da página bloqueiam a renderização, a LCP é afetada.

A solução é mudar o markup da imagem:

<img src="/sand-wasp.jpg" alt="Sand Wasp" width="384" height="255">

Esse é o padrão ideal para imagens que estão na viewport durante a inicialização, porque o scanner de pré-carregamento vai descobrir e buscar o recurso de imagem mais rapidamente.

Um gráfico de cascata de rede do WebPageTest que mostra um cenário de carregamento de uma imagem na viewport durante a inicialização. A imagem não é carregada de forma lenta, o que significa que ela não depende do script para ser carregada. Isso significa que o scanner de pré-carregamento pode detectá-la mais cedo.
Figura 9. Diagrama em cascata de rede do WebPageTest de uma página da Web executada no Chrome em um dispositivo móvel em uma conexão 3G simulada. O scanner de pré-carregamento descobre o recurso de imagem antes do início do carregamento do CSS e do JavaScript, o que dá ao navegador uma vantagem no carregamento.

O resultado neste exemplo simplificado é uma melhoria de 100 milissegundos no LCP em uma conexão lenta. Isso pode não parecer uma grande melhoria, mas é quando você considera que a solução é uma correção rápida de marcação e que a maioria das páginas da Web é mais complexa do que este conjunto de exemplos. Isso significa que os candidatos a LCP podem ter que lidar com a largura de banda com muitos outros recursos, de modo que otimizações como essa se tornam cada vez mais importantes.

Imagens de plano de fundo CSS

O scanner de pré-carregamento do navegador verifica markup. Ele não verifica outros tipos de recursos, como CSS, que podem envolver buscas de imagens referenciadas pela propriedade background-image.

Assim como o HTML, os navegadores processam o CSS no próprio modelo de objetos, conhecido como CSSOM. Se recursos externos forem descobertos durante a criação do CSSOM, esses recursos serão solicitados no momento da descoberta, e não pelo verificador de pré-carregamento.

Digamos que o candidato à LCP da sua página seja um elemento com uma propriedade CSS background-image. Confira o que acontece quando os recursos são carregados:

Um gráfico de cascata da rede WebPageTest mostrando uma página com um candidato a LCP carregado do CSS usando a propriedade de imagem de plano de fundo. Como a imagem candidata do LCP está em um tipo de recurso que o scanner de pré-carregamento do navegador não pode examinar, o carregamento do recurso é atrasado até que o CSS seja transferido por download e processado, atrasando o tempo de pintura do candidato do LCP.
Figura 10. Diagrama em cascata de rede do WebPageTest de uma página da Web executada no Chrome em um dispositivo móvel em uma conexão 3G simulada. O candidato à LCP da página é um elemento com uma propriedade CSS background-image (linha 3). A imagem solicitada não começa a ser buscada até que o analisador de CSS a encontre.

Nesse caso, o scanner de pré-carregamento não é derrotado, mas não está envolvido. Mesmo assim, se um candidato ao LCP na página for de uma propriedade CSS background-image, você vai querer pré-carregar essa imagem:

<!-- Make sure this is in the <head> below any
     stylesheets, so as not to block them from loading -->
<link rel="preload" as="image" href="lcp-image.jpg">

Essa dica rel=preload é pequena, mas ajuda o navegador a descobrir a imagem mais rapidamente:

Um gráfico de cascata de rede do WebPageTest mostrando uma imagem de plano de fundo CSS (que é o candidato do LCP) carregando muito mais rápido devido ao uso de uma sugestão rel=preload. O tempo de LCP melhora em cerca de 250 milissegundos.
Figura 11. Diagrama em cascata de rede do WebPageTest de uma página da Web executada no Chrome em um dispositivo móvel em uma conexão 3G simulada. O candidato à LCP da página é um elemento com uma propriedade CSS background-image (linha 3). A dica rel=preload ajuda o navegador a descobrir a imagem cerca de 250 milissegundos antes do que sem a dica.

Com a dica rel=preload, o candidato à LCP é descoberto mais cedo, reduzindo o tempo da LCP. Essa dica ajuda a corrigir o problema, mas a melhor opção pode ser avaliar se o candidato de LCP da imagem precisa ser carregado do CSS. Com uma tag <img>, você terá mais controle sobre o carregamento de uma imagem adequada para a viewport, permitindo que o scanner de pré-carregamento a detecte.

Muitos recursos em linha

A inserção inline é uma prática que coloca um recurso dentro do HTML. É possível inserir folhas de estilo em elementos <style>, scripts em elementos <script> e praticamente qualquer outro recurso usando a codificação base64.

Colocar recursos inline pode ser mais rápido do que fazer o download deles porque uma solicitação separada não é emitida para o recurso. Ele está no documento e é carregado instantaneamente. No entanto, existem desvantagens significativas:

  • Se você não estiver armazenando o HTML em cache, e não puder fazer isso se a resposta do HTML for dinâmica, os recursos inline nunca serão armazenados em cache. Isso afeta o desempenho porque os recursos em linha não são reutilizáveis.
  • Mesmo que você possa armazenar HTML em cache, os recursos inline não são compartilhados entre documentos. Isso reduz a eficiência do armazenamento em cache em comparação com arquivos externos que podem ser armazenados em cache e reutilizados em toda a origem.
  • Se você inlinear muito, o scanner de pré-carregamento vai demorar mais para descobrir os recursos mais adiante no documento, porque o download desse conteúdo extra inline vai demorar mais.

Confira esta página como exemplo. Em determinadas condições, o candidato do LCP é a imagem na parte de cima da página, e o CSS está em um arquivo separado carregado por um elemento <link>. A página também usa quatro fontes da Web que são solicitadas como arquivos separados do recurso CSS.

Um gráfico de cascata de rede do WebPageTest com um arquivo CSS externo que tem quatro fontes referenciadas. A imagem candidata do LCP é descoberta pelo scanner de pré-carregamento no devido tempo.
Fig. 12:um gráfico de cascata de rede do WebPageTest de uma página da Web, executado no Chrome em um dispositivo móvel com uma conexão 3G simulada. O candidato do LCP da página é uma imagem carregada de um elemento <img>, mas é descoberto pelo scanner de pré-carregamento porque o CSS e as fontes necessárias para o carregamento da página estão em recursos separados, o que não atrasa o scanner de pré-carregamento.

O que acontece se o CSS e todas as fontes forem inline como recursos base64?

Um gráfico de cascata de rede do WebPageTest com um arquivo CSS externo que tem quatro fontes referenciadas. O scanner de pré-carregamento tem um atraso significativo na descoberta da imagem LCP .
Figura 13. Diagrama em cascata de rede do WebPageTest de uma página da Web executada no Chrome em um dispositivo móvel em uma conexão 3G simulada. O candidato do LCP da página é uma imagem carregada de um elemento <img>, mas o inline do CSS e dos quatro recursos de fonte no " atrasam o scanner de pré-carregamento de descobrir a imagem até que esses recursos sejam totalmente transferidos por download.

O impacto do inline gera consequências negativas para o LCP nesse exemplo e para a performance em geral. A versão da página que não inline nada pinta a imagem LCP em cerca de 3,5 segundos. A página que inline tudo não pinta a imagem LCP até pouco mais de 7 segundos.

Há mais em jogo do que apenas o scanner de pré-carregamento. Inserir fontes não é uma boa estratégia porque o base64 é um formato ineficiente para recursos binários. Outro fator em jogo é que os recursos de fontes externos não são transferidos por download, a menos que sejam considerados necessários pelo CSSOM. Quando essas fontes são inline como base64, elas são baixadas, independentemente de serem necessárias para a página atual ou não.

Um pré-carregamento poderia melhorar as coisas aqui? Claro. É possível carregar a imagem do LCP com antecedência e reduzir o tempo do LCP, mas o aumento do HTML potencialmente não armazenável em cache com recursos inline tem outras consequências negativas para o desempenho. A First Contentful Paint (FCP) também é afetada por esse padrão. Na versão da página em que nada está inline, a FCP é de aproximadamente 2,7 segundos. Na versão em que tudo está inline, o FCP é de aproximadamente 5,8 segundos.

Tenha muito cuidado ao inserir elementos no HTML, especialmente recursos codificados em base64. Em geral, isso não é recomendado, exceto para recursos muito pequenos. Use o mínimo possível de inline, porque usar muito é perigoso.

Renderização da marcação com JavaScript do lado do cliente

Não há dúvidas: o JavaScript definitivamente afeta a velocidade da página. Os desenvolvedores dependem dele para oferecer interatividade, mas também há uma tendência de depender dele para entregar o conteúdo. Em alguns aspectos, isso aprimora a experiência do desenvolvedor, mas os benefícios para os desenvolvedores nem sempre se traduzem em benefícios para os usuários.

Um padrão que pode derrotar o scanner de pré-carregamento é renderizar marcação com JavaScript do lado do cliente:

Uma hierarquia de rede do WebPageTest mostrando uma página básica com imagens e texto renderizados completamente no cliente em JavaScript. Como a marcação está contida no JavaScript, o scanner de pré-carregamento não pode detectar nenhum dos recursos. Todos os recursos estão atrasados adicionalmente devido à rede extra e ao tempo de processamento que as estruturas JavaScript exigem.
Fig. 14:um gráfico de cascata de rede do WebPageTest de uma página da Web renderizada pelo cliente é executado no Chrome em um dispositivo móvel com uma conexão 3G simulada. Como o conteúdo está contido em JavaScript e depende de uma estrutura para renderização, o recurso de imagem na marcação renderizada pelo cliente fica oculto do scanner de pré-carregamento. A experiência equivalente renderizada pelo servidor é mostrada na Fig. 9.

Quando os payloads de marcação são contidos e renderizados inteiramente pelo JavaScript no navegador, todos os recursos dessa marcação ficam efetivamente invisíveis para o scanner de pré-carregamento. Isso atrasa a descoberta de recursos importantes, o que certamente afeta o LCP. No caso desses exemplos, a solicitação da imagem do LCP é significativamente atrasada em comparação com a experiência equivalente renderizada pelo servidor que não exige JavaScript para aparecer.

Isso desvia um pouco do foco deste artigo, mas os efeitos da renderização de marcação no cliente vão muito além de derrotar o scanner de pré-carregamento. Por exemplo, a introdução do JavaScript para oferecer uma experiência que não exige isso introduz um tempo de processamento desnecessário que pode afetar a Interaction to Next Paint (INP). Renderizar quantidades muito grandes de marcação no cliente tem maior probabilidade de gerar tarefas longas em comparação com a mesma quantidade de marcação enviada pelo servidor. O motivo para isso, além do processamento extra que o JavaScript envolve, é que os navegadores transmitem a marcação do servidor e dividem a renderização de uma forma que tende a limitar tarefas longas. A marcação renderizada pelo cliente, por outro lado, é tratada como uma tarefa única e monolítica, que pode afetar o INP de uma página.

A solução para esse cenário depende da resposta a esta pergunta: Há um motivo para a marcação da sua página não poder ser fornecida pelo servidor em vez de ser renderizada no cliente? Se a resposta for "não", a renderização do lado do servidor (SSR, na sigla em inglês) ou a marcação gerada estaticamente deve ser considerada sempre que possível, porque ajuda o scanner de pré-carregamento a descobrir e buscar recursos importantes com antecedência.

Se a página precisar de JavaScript para anexar funcionalidades a algumas partes do markup, ainda será possível fazer isso com o SSR, seja com JavaScript vanilla ou com hidratação para aproveitar o melhor dos dois mundos.

Ajudar o scanner de pré-carregamento a ajudar você

O scanner de pré-carregamento é uma otimização de navegador altamente eficaz que ajuda as páginas a carregar mais rápido durante a inicialização. Ao evitar padrões que impedem a descoberta de recursos importantes com antecedência, você não apenas simplifica o desenvolvimento, mas também cria experiências do usuário melhores que vão gerar resultados melhores em muitas métricas, incluindo algumas Core Web Vitals.

Para recapitular, confira o que você precisa saber desta postagem:

  • O verificador de pré-carregamento do navegador é um analisador HTML secundário que analisa o processo antes do principal caso ele esteja bloqueado para descobrir, de forma oportuna, recursos que pode buscar mais cedo.
  • Os recursos que não estiverem presentes na marcação fornecida pelo servidor na solicitação de navegação inicial não podem ser descobertos pelo scanner de pré-carregamento. Confira algumas maneiras de impedir o verificador de pré-carregamento, entre outras:
    • Injete recursos no DOM com JavaScript, sejam scripts, imagens, folhas de estilo ou qualquer outra coisa que seja melhor no payload de marcação inicial do servidor.
    • Carregamento lento de imagens acima da dobra ou iframes usando uma solução JavaScript.
    • Renderizar marcação no cliente que pode conter referências a subrecursos do documento usando JavaScript.
  • O scanner de pré-carregamento só verifica HTML. Ele não examina o conteúdo de outros recursos, principalmente CSS, que podem incluir referências a recursos importantes, incluindo candidatos de LCP.

Se, por algum motivo, você não conseguir evitar um padrão que afete negativamente a capacidade do scanner de pré-carregamento de acelerar o desempenho de carregamento, considere a dica de recurso rel=preload. Caso você use rel=preload, teste nas ferramentas do laboratório para verificar se ele está gerando o efeito desejado. Por fim, não pré-carregue muitos recursos, porque, quando você prioriza tudo, nada é priorizado.

Recursos

Imagem principal do Unsplash, por Mohammad Rahmani .