No módulo anterior, vimos alguma teoria por trás do caminho crítico de renderização e como os recursos de bloqueio de renderização e de análise podem atrasar a renderização inicial de uma página. Agora que você entende parte da teoria por trás disso, está pronto para aprender algumas técnicas para otimizar o caminho crítico de renderização.
À medida que uma página é carregada, muitos recursos são referenciados no HTML que fornecem a página com aparência e layout usando CSS, bem como interatividade usando JavaScript. Neste módulo, são abordados vários conceitos importantes relacionados a esses recursos e como eles afetam o tempo de carregamento de uma página.
Bloqueio da renderização
Conforme discutido no módulo anterior, o CSS é um recurso de bloqueio de renderização, porque impede que o navegador renderize conteúdo até que o modelo de objeto CSS (CSSOM, na sigla em inglês) seja criado. O navegador bloqueia a renderização para evitar um Flash de conteúdo sem estilo (FOUC, na sigla em inglês), o que é indesejável do ponto de vista da experiência do usuário.
No vídeo anterior, há um breve FOUC em que você pode ver a página sem nenhum estilo. Em seguida, todos os estilos serão aplicados quando o CSS da página terminar de carregar na rede, e a versão sem estilo da página será imediatamente substituída pela versão com estilo.
De modo geral, um FOUC é algo que você normalmente não vê, mas o conceito é importante para entender por que o navegador bloqueia a renderização da página até que o CSS seja transferido por download e aplicado à página. O bloqueio da renderização não é necessariamente indesejável, mas é importante minimizar o tempo de duração mantendo seu CSS otimizado.
Bloqueio do analisador
Um recurso de bloqueio do analisador interrompe o analisador de HTML, como um elemento <script>
sem atributos async
ou defer
. Quando o analisador encontra um
elemento <script>
, o navegador precisa avaliar e executar o script antes
de prosseguir com a análise do restante do HTML. Isso ocorre por padrão, já que os scripts podem modificar ou acessar o DOM durante um período enquanto ele ainda está sendo criado.
<!-- This is a parser-blocking script: -->
<script src="/script.js"></script>
Ao usar arquivos JavaScript externos (sem async
ou defer
), o analisador é
bloqueado do momento em que o arquivo é descoberto até que ele seja transferido por download, analisado e
executado. Ao usar o JavaScript inline, o analisador é bloqueado de forma semelhante até
que o script in-line seja analisado e executado.
O scanner de pré-carregamento
O scanner de pré-carregamento é uma otimização de navegador na forma de um analisador de HTML
secundário que verifica a resposta HTML bruta para encontrar e buscar recursos
de modo especulativo antes que o analisador HTML principal os descubra. Por
exemplo, o verificador de pré-carregamento permite que o navegador inicie o download de um
recurso especificado em um elemento <img>
, mesmo quando o analisador de HTML é bloqueado
ao buscar e processar recursos como CSS e JavaScript.
Para aproveitar o verificador de pré-carregamento, os recursos essenciais precisam ser incluídos na marcação HTML enviada pelo servidor. Os seguintes padrões de carregamento de recursos não são detectáveis pelo verificador de pré-carregamento:
- Imagens carregadas pelo CSS usando a propriedade
background-image
. Essas referências de imagem estão em CSS e não podem ser descobertas pelo scanner de pré-carregamento. - Scripts carregados dinamicamente na forma de marcação de elemento
<script>
injetada no DOM usando JavaScript ou módulos carregados usandoimport()
dinâmico. - HTML renderizado no cliente usando JavaScript. Essa marcação fica contida em strings em recursos JavaScript e não é detectável pelo verificador de pré-carregamento.
- Declarações
@import
CSS.
Esses padrões de carregamento de recursos são todos recursos descobertos tardiamente e, portanto, não se beneficiam do verificador de pré-carregamento. Evite-as sempre que possível. No entanto, se
não for possível evitar esses padrões, use uma
dica preload
para evitar atrasos na descoberta de recursos.
CSS
O CSS determina a apresentação e o layout de uma página. Conforme descrito anteriormente, o CSS é um recurso de bloqueio de renderização. Portanto, otimizar seu CSS pode ter um impacto considerável no tempo geral de carregamento da página.
Minificação
Minimizar arquivos CSS reduz o tamanho do arquivo de um recurso CSS, tornando o download mais rápido. Isso é feito principalmente removendo o conteúdo de um arquivo CSS de origem, como espaços e outros caracteres invisíveis, e enviando o resultado para um arquivo recém-otimizado:
/* Unminified CSS: */
/* Heading 1 */
h1 {
font-size: 2em;
color: #000000;
}
/* Heading 2 */
h2 {
font-size: 1.5em;
color: #000000;
}
/* Minified CSS: */
h1,h2{color:#000}h1{font-size:2em}h2{font-size:1.5em}
Na forma mais básica, a minificação do CSS é uma otimização eficaz que pode melhorar a FCP do seu site e talvez até mesmo a LCP em alguns casos. Ferramentas como bundlers podem executar essa otimização automaticamente em builds de produção.
Remover CSS não utilizado
Antes de renderizar qualquer conteúdo, o navegador precisa fazer o download e analisar todas as folhas de estilo. O tempo necessário para concluir a análise também inclui estilos que não são usados na página atual. Se você usa um bundler que combina todos os recursos CSS em um único arquivo, seus usuários provavelmente estão fazendo o download de mais CSS do que o necessário para renderizar a página atual.
Para descobrir CSS não utilizado na página atual, use a Ferramenta de cobertura no Chrome DevTools.
![Uma captura de tela da ferramenta de cobertura no Chrome DevTools. Um arquivo CSS está selecionado no painel inferior, mostrando uma quantidade considerável de CSS não utilizado pelo layout da página atual.](https://web.dev/static/learn/performance/optimize-resource-loading/image/fig-1.png?authuser=5&hl=pt)
A remoção do CSS não utilizado tem um efeito duplo: além de reduzir o tempo de download, você está otimizando a construção da árvore de renderização, porque o navegador precisa processar menos regras de CSS.
Evitar declarações @import
CSS
Pode parecer conveniente, mas evite declarações @import
no CSS:
/* Don't do this: */
@import url('style.css');
Da mesma forma que o elemento <link>
funciona no HTML, a declaração @import
no CSS permite importar um recurso CSS externo de uma folha de estilos. A principal diferença entre essas duas abordagens é que o elemento HTML <link>
faz parte da resposta HTML e, portanto, é descoberto muito antes do download de um arquivo CSS por uma declaração @import
.
Isso ocorre porque, para que uma declaração @import
seja
descoberta, é necessário fazer o download primeiro do arquivo CSS que a contém. Isso
resulta no que é conhecido como cadeia de solicitações que, no caso do CSS, atrasa
o tempo necessário para uma página ser renderizada inicialmente. Outra desvantagem é que as planilhas de estilo carregadas usando uma declaração @import
não podem ser descobertas pelo verificador de pré-carregamento e, portanto, se tornam recursos de bloqueio de renderização descobertos tardiamente.
<!-- Do this instead: -->
<link rel="stylesheet" href="style.css">
Na maioria dos casos, é possível substituir o @import
usando um
elemento <link rel="stylesheet">
. Os elementos <link>
permitem o download das folhas de estilo simultaneamente e reduz o tempo total de carregamento, diferente das declarações @import
, que fazem o download delas consecutivamente.
CSS essencial inline
O tempo necessário para fazer o download dos arquivos CSS pode aumentar a FCP da página. A inserção
de estilos críticos no documento <head>
elimina a solicitação de rede para um
recurso CSS e, quando feita corretamente, pode melhorar os tempos de carregamento inicial quando o
cache do navegador do usuário não está preparado. O CSS restante pode ser carregado
de forma assíncrona ou anexado ao final do elemento <body>
.
<head>
<title>Page Title</title>
<!-- ... -->
<style>h1,h2{color:#000}h1{font-size:2em}h2{font-size:1.5em}</style>
</head>
<body>
<!-- Other page markup... -->
<link rel="stylesheet" href="non-critical.css">
</body>
A desvantagem é que uma grande quantidade de CSS em linha adiciona mais bytes à resposta HTML inicial. Como os recursos HTML geralmente não podem ser armazenados em cache por muito tempo (ou muito), isso significa que o CSS in-line não é armazenado em cache para páginas subsequentes que podem usar o mesmo CSS em folhas de estilo externas. Teste e meça o desempenho da sua página para garantir que as compensações valem o esforço.
Demonstrações de CSS
JavaScript
O JavaScript impulsiona a maior parte da interatividade na Web, mas tem um custo. O envio de muito JavaScript pode deixar a resposta da página da Web mais lenta durante o carregamento e até mesmo causar problemas de capacidade de resposta que atrasam as interações. Ambos os fatores podem ser frustrantes para os usuários.
JavaScript de bloqueio de renderização
Ao carregar elementos <script>
sem os atributos defer
ou async
, o
navegador bloqueia a análise e a renderização até que o script seja transferido por download, analisado e
executado. Da mesma forma, os scripts in-line bloqueiam o analisador até que o script seja analisado
e executado.
async
x defer
async
e defer
permitem que scripts externos sejam carregados sem bloquear o analisador
de HTML, enquanto os scripts (incluindo scripts in-line) com type="module"
são
adiados automaticamente. No entanto, async
e defer
têm algumas diferenças
importantes de entender.
Os scripts carregados com async
são analisados e executados imediatamente após o download, enquanto os scripts carregados com defer
são executados quando a análise do documento HTML é concluída. Isso ocorre ao mesmo tempo que o evento DOMContentLoaded
do navegador.
Além disso, os scripts async
podem ser executados fora de ordem, enquanto os scripts defer
são executados na ordem em que aparecem na marcação.
Renderização do lado do cliente
De modo geral, evite usar o JavaScript para renderizar qualquer conteúdo crítico ou o elemento LCP de uma página. Isso é conhecido como renderização no lado do cliente, e é uma técnica muito usada em aplicativos de página única (SPAs).
A marcação renderizada por JavaScript evita o verificador de pré-carregamento, já que os recursos contidos na marcação renderizada pelo cliente não são detectáveis por ela. Isso pode atrasar o download de recursos cruciais, como uma imagem LCP. O navegador só começa a fazer o download da imagem LCP após a execução do script e a adição do elemento ao DOM. Por sua vez, o script só pode ser executado depois de ser descoberto, transferido por download e analisado. Isso é conhecido como cadeia de solicitação crítica e precisa ser evitado.
Além disso, a renderização de marcações usando JavaScript tem mais probabilidade de gerar tarefas longas do que a marcação transferida por download do servidor em resposta a uma solicitação de navegação. O uso extensivo da renderização de HTML do lado do cliente pode afetar negativamente a latência de interação. Isso acontece principalmente nos casos em que o DOM de uma página é muito grande, o que aciona um trabalho de renderização significativo quando o JavaScript modifica o DOM.
Minificação
Semelhante ao CSS, a minificação de JavaScript diminui o tamanho do arquivo de um recurso de script. Isso pode acelerar os downloads, permitindo que o navegador passe para o processo de análise e compilação de JavaScript mais rapidamente.
Além disso, a minificação de JavaScript vai além da minificação de outros recursos, como CSS. Quando o JavaScript é reduzido, ele não é apenas removido de elementos como espaços, guias e comentários, mas os símbolos no JavaScript de origem são encurtados. Esse processo às vezes é conhecido como uglificação. Para ver a diferença, use este código-fonte JavaScript:
// Unuglified JavaScript source code:
export function injectScript () {
const scriptElement = document.createElement('script');
scriptElement.src = '/js/scripts.js';
scriptElement.type = 'module';
document.body.appendChild(scriptElement);
}
Quando o código-fonte JavaScript anterior é uglificado, o resultado pode ser semelhante a este snippet de código:
// Uglified JavaScript production code:
export function injectScript(){const t=document.createElement("script");t.src="/js/scripts.js",t.type="module",document.body.appendChild(t)}
No snippet anterior, é possível observar que a variável legível scriptElement
na origem é encurtada para t
. Quando aplicada em um grande conjunto de scripts, a economia pode ser bastante significativa, sem afetar os recursos que o JavaScript de produção de um site da Web oferece.
Se você estiver usando um bundler para processar o código-fonte do site, a uglificação costuma ser feita automaticamente em builds de produção. Uglificadores, como Terser, por exemplo, também são altamente configuráveis, o que permite ajustar a agressividade do algoritmo de uglificação para conseguir o máximo de economia. No entanto, os padrões de qualquer ferramenta de uglificação geralmente são suficientes para encontrar o equilíbrio certo entre o tamanho da saída e a preservação dos recursos.
Demonstrações de JavaScript
testar seus conhecimentos
Qual é a melhor maneira de carregar vários arquivos CSS no navegador?
@import
.<link>
.O que o scanner de pré-carregamento do navegador faz?
<link rel="preload">
em
um recurso HTML.
Por que o navegador bloqueia temporariamente a análise de HTML por padrão ao fazer o download de recursos JavaScript?
A seguir: ajudar o navegador com dicas de recursos
Agora que você sabe como os recursos carregados no elemento <head>
podem
afetar o carregamento inicial da página e várias métricas, é hora de continuar. No próximo
módulo, vamos conhecer as dicas de recursos e mostrar como elas podem dar dicas valiosas para
o navegador começar a carregar recursos e abrir conexões com servidores de origem
cruzada mais cedo do que o navegador faria sem elas.