No módulo anterior, abordamos um pouco da teoria por trás do caminho de renderização crítico foi explorado, e como os recursos que bloqueiam a renderização e o analisador podem atrasar a renderização inicial de uma página. Agora que você já entendeu um pouco da teoria, está pronto para aprender algumas técnicas de otimização do caminho de renderização crítico.
À medida que uma página é carregada, muitos recursos são referenciados no HTML, que fornece a aparência e o layout da página por meio de CSS, bem como a interatividade por JavaScript. Neste módulo, abordamos vários conceitos importantes relacionados a esses recursos e como eles afetam o tempo de carregamento de uma página.
Bloqueio de renderização
Como discutimos no módulo anterior, o CSS é um recurso que bloqueia a renderização, já que impede que o navegador renderize qualquer conteúdo até que o modelo de objeto CSS (CSSOM) seja construído. O navegador bloqueia a renderização para evitar um Flash de conteúdo sem estilo (FOUC), 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 são aplicados quando o CSS da página termina de carregar da rede, e a versão sem estilo da página é imediatamente substituída pela versão estilizada.
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 baixado e aplicado a ela. O bloqueio de renderização não é necessariamente indesejável, mas você precisa minimizar a duração dele mantendo o CSS otimizado.
Bloqueio do analisador
Um recurso que bloqueia o analisador interrompe o analisador de HTML, como um <script>
elemento sem atributos async ou defer. Quando o analisador encontra um
<script> elemento, o navegador precisa avaliar e executar o script antes de
continuar a análise do restante do HTML. Isso é proposital, já que os scripts podem modificar ou acessar o DOM durante um período em que ele ainda está sendo construído.
<!-- This is a parser-blocking script: -->
<script src="/script.js"></script>
Ao usar arquivos JavaScript externos (sem async ou defer ou type=module, que é defer por padrão), o analisador é bloqueado desde o momento em que o arquivo é descoberto até que seja baixado, analisado e executado. Ao usar JavaScript inline, o analisador é bloqueado de maneira semelhante até que o script inline seja analisado e executado.
O scanner de pré-carregamento
O scanner de pré-carregamento é uma otimização do navegador na forma de um analisador de HTML secundário
que verifica a resposta HTML bruta para encontrar e buscar recursos especulativamente
antes que o analisador de HTML principal os descubra. Por
exemplo, o scanner de pré-carregamento permite que o navegador comece a baixar um
recurso especificado em um <img> elemento, mesmo quando o analisador de HTML está bloqueado
durante a busca e o processamento de recursos como CSS e JavaScript.
Para aproveitar o scanner de pré-carregamento, os recursos críticos precisam ser incluídos na marcação HTML enviada pelo servidor. Os padrões de carregamento de recursos a seguir não podem ser descobertos pelo scanner 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 elementos
<script>injetados no DOM usando JavaScript ou módulos carregados usando dinâmicoimport(). - HTML renderizado no cliente usando JavaScript. Essa marcação está contida em strings em recursos JavaScript e não pode ser descoberta pelo scanner de pré-carregamento.
- Declarações
@importde CSS.
Esses padrões de carregamento de recursos são todos recursos descobertos tarde e, portanto, não se beneficiam do scanner de pré-carregamento. Evite-os sempre que possível. No entanto, se
evitar esses padrões não for possível, você poderá usar uma dica
preload para evitar atrasos na descoberta de recursos.
CSS
O CSS determina a apresentação e o layout de uma página. Como descrito anteriormente, o CSS é um recurso que bloqueia a renderização. Portanto, otimizar o CSS pode ter um impacto considerável no tempo de carregamento da página.
Minificação
A minificação de arquivos CSS reduz o tamanho do arquivo de um recurso CSS, tornando o download mais rápido. Isso é feito principalmente removendo conteúdo de um arquivo CSS de origem, como espaços e outros caracteres invisíveis, e gerando o resultado em 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 de CSS é uma otimização eficaz que pode melhorar o FCP do seu site e, talvez, até mesmo o LCP em alguns casos. Ferramentas como bundlers podem realizar essa otimização automaticamente para você em builds de produção.
Remova CSS não utilizado
Antes de renderizar qualquer conteúdo, o navegador precisa baixar e analisar todas as folhas de estilo. O tempo necessário para concluir a análise também inclui estilos não utilizados na página atual. Se você estiver usando um bundler que combina todos os recursos CSS em um único arquivo, é provável que seus usuários estejam baixando mais CSS do que o necessário para renderizar a página atual.
Para descobrir o CSS não utilizado da página atual, use a ferramenta de cobertura no Chrome DevTools.
A remoção de 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, já que o navegador precisa processar menos regras de CSS.
Evite declarações @import de CSS
Embora possa parecer conveniente, evite declarações @import no CSS:
/* Don't do this: */
@import url('style.css');
Assim como o elemento <link> funciona em HTML, a declaração @import
em CSS permite importar um recurso CSS externo de uma folha de estilo. A
principal diferença entre essas duas abordagens é que o elemento HTML <link>
faz parte da resposta HTML e, portanto, é descoberto muito mais cedo do que um arquivo CSS
baixado por uma declaração @import.
O motivo é que, para que uma declaração @import seja descoberta, o arquivo CSS que a contém precisa primeiro ser baixado. Isso resulta no que é conhecido como uma cadeia de solicitações que, no caso do CSS, atrasa o tempo necessário para que uma página seja renderizada inicialmente. Outra desvantagem é que as folhas de estilo carregadas usando uma declaração @import não podem ser descobertas pelo scanner de pré-carregamento e, portanto, se tornam recursos que bloqueiam a renderização descobertos tarde.
<!-- Do this instead: -->
<link rel="stylesheet" href="style.css">
Na maioria dos casos, é possível substituir o @import usando um
<link rel="stylesheet"> elemento. <link> elementos permitem que as folhas de estilo sejam
baixadas simultaneamente e reduzem o tempo geral de carregamento, ao contrário das @import
declarações, que baixam as folhas de estilo consecutivamente.
CSS crítico inline
O tempo necessário para baixar arquivos CSS pode aumentar o FCP de uma página. A incorporaçã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 iniciais quando o
cache do navegador de um 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>
Por outro lado, a incorporação de uma grande quantidade de CSS adiciona mais bytes à resposta HTML inicial. Como os recursos HTML geralmente não podem ser armazenados em cache por muito tempo ou de forma alguma, isso significa que o CSS incorporado não é armazenado em cache para páginas subsequentes que podem usar o mesmo CSS em folhas de estilo externas. Teste e meça a performance da sua página para garantir que as compensações valham a pena.
Demonstrações de CSS
JavaScript
O JavaScript impulsiona a maior parte da interatividade na Web, mas tem um custo. O envio de JavaScript em excesso pode tornar sua página da Web lenta para responder durante o carregamento e até mesmo causar problemas de capacidade de resposta que diminuem as interações. Ambos podem ser frustrantes para os usuários.
JavaScript que bloqueia a 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 baixado, analisado e
executado. Da mesma forma, os scripts inline 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 scripts (incluindo scripts inline) com type="module" são
adiados automaticamente. No entanto, async e defer têm algumas diferenças importantes.
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
Geralmente, evite usar JavaScript para renderizar qualquer conteúdo crítico ou o elemento LCP de uma página. Isso é conhecido como renderização do lado do cliente e é uma técnica usada extensivamente em aplicativos de página única (SPAs).
A marcação renderizada pelo JavaScript ignora o scanner de pré-carregamento, já que os recursos contidos na marcação renderizada pelo cliente não podem ser descobertos por ele. Isso pode atrasar o download de recursos importantes, como uma imagem LCP. O navegador só começa a baixar a imagem LCP depois que o script é executado e adiciona o elemento ao DOM. Por sua vez, o script só pode ser executado depois de ser descoberto, baixado e analisado. Isso é conhecido como uma cadeia de solicitações críticas e precisa ser evitado.
Além disso, a marcação de renderização usando JavaScript tem mais probabilidade de gerar tarefas longas do que a marcação baixada do servidor em resposta a uma solicitação de navegação. O uso extensivo da renderização do lado do cliente de HTML pode afetar negativamente a latência de interação. Isso é especialmente verdadeiro em 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 reduz o tamanho do arquivo de um recurso de script. Isso pode levar a downloads mais rápidos, 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 um passo além da minificação de outros recursos, como CSS. Quando o JavaScript é minificado, 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 uglification. Para ver a diferença, confira o código-fonte JavaScript a seguir:
// 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 ao snippet de código a seguir:
// 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 notar que a variável legível
scriptElement na origem é encurtada para t. Quando aplicado a uma grande coleção de scripts, as economias podem ser bastante significativas, sem afetar os recursos fornecidos pelo JavaScript de produção de um site.
Se você estiver usando um bundler para processar o código-fonte do seu site, a uglification geralmente será feita automaticamente para builds de produção. Os uglificadores, como o Terser, por exemplo, também são altamente configuráveis, o que permite ajustar a agressividade do algoritmo de uglification para alcançar a economia máxima. No entanto, os padrões de qualquer ferramenta de uglification 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
Teste seus conhecimentos
Qual é a melhor maneira de carregar vários arquivos CSS no navegador?
@import de CSS.<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 baixar recursos JavaScript?
Próxima etapa: ajudar o navegador com dicas de recursos
Agora que você já sabe como os recursos carregados no elemento <head> podem afetar o carregamento de página inicial e várias métricas, é hora de continuar. No próximo
módulo, vamos explorar as dicas de recursos e como elas podem dar dicas valiosas ao
navegador para começar a carregar recursos e abrir conexões com servidores de origem cruzada
mais cedo do que o navegador faria sem elas.