Melhorar a Largest Contentful Paint no ecossistema JavaScript.
Como parte do projeto Aurora, o Google tem trabalhado com frameworks da Web conhecidos para garantir que eles tenham bom desempenho de acordo com as Core Web Vitals. O Angular e o Next.js já têm a formatação de fonte inline, explicada na primeira parte deste artigo. A segunda otimização que vamos abordar é o inline CSS crítico, que agora é ativado por padrão no Angular CLI e tem uma implementação em andamento no Nuxt.js.
Inlining de fontes
Depois de analisar centenas de aplicativos, a equipe do Aurora descobriu que os desenvolvedores geralmente incluem fontes
nos aplicativos referenciando-as no elemento <head>
de index.html
. Confira um exemplo
de como isso ficaria ao incluir os ícones do Material Design:
<!doctype html>
<html lang="en">
<head>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
...
</html>
Mesmo que esse padrão seja totalmente válido e funcional, ele bloqueia a renderização do aplicativo e introduz uma solicitação extra. Para entender melhor o que está acontecendo, confira o código-fonte da folha de estilo referenciada no HTML acima:
/* fallback */
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/font.woff2) format('woff2');
}
.material-icons {
/*...*/
}
Observe como a definição de font-face
faz referência a um arquivo externo hospedado em fonts.gstatic.com
.
Ao carregar o aplicativo, o navegador precisa primeiro fazer o download da folha de estilo original referenciada
no cabeçalho.
Em seguida, o navegador faz o download do arquivo woff2
e, por fim, pode continuar a renderização do
aplicativo.
Uma oportunidade de otimização é fazer o download da folha de estilo inicial no tempo de build e in-line no
index.html
. Isso pula uma ida e volta inteira para o CDN no momento da execução, reduzindo o tempo de bloqueio.
Ao criar o aplicativo, uma solicitação é enviada para a CDN, que busca a folha de estilo e a inline
no arquivo HTML, adicionando um <link rel=preconnect>
ao domínio. Ao aplicar essa técnica, teríamos
o seguinte resultado:
<!doctype html>
<html lang="en">
<head>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin >
<style type="text/css">
@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/font.woff2) format('woff2');}.material-icons{/*...*/}</style>
...
</html>
O recurso inline de fontes já está disponível no Next.js e no Angular
Quando os desenvolvedores de frameworks implementam a otimização nas ferramentas, eles facilitam a ativação de aplicativos novos e existentes, trazendo melhorias para todo o ecossistema.
Essa melhoria é ativada por padrão na Next.js v10.2 e na Angular v11. Ambos oferecem suporte a fontes inline do Google e da Adobe. O Angular pretende introduzir o segundo na v12.2.
Você pode encontrar a implementação da inlining de fontes no Next.js no GitHub e conferir o vídeo que explica essa otimização no contexto do Angular.
Inserir CSS essencial
Outro aprimoramento envolve melhorar as métricas First Contentful Paint (FCP) e Largest Contentful Paint (LCP) com a incorporação de CSS crítico. O CSS essencial de uma página inclui todos os estilos usados na renderização inicial. Para saber mais sobre o assunto, consulte Defer CSS não críticos.
Observamos que muitos aplicativos estão carregando estilos de forma síncrona, o que bloqueia a renderização
do aplicativo. Uma solução rápida é carregar os estilos de forma assíncrona. Em vez de carregar os scripts com
media="all"
, defina o valor do atributo media
como print
e, quando o carregamento for concluído,
substitua o valor do atributo por all
:
<link rel="stylesheet" href="..." media="print" onload="this.media='all'">
No entanto, essa prática pode causar a oscilação de conteúdo sem estilo.
O vídeo acima mostra a renderização de uma página, que carrega os estilos de forma assíncrona. Essa oscilação acontece porque o navegador começa a fazer o download dos estilos e depois renderiza o HTML depois. Depois que o navegador faz o download dos estilos, ele aciona o evento onload
do elemento de link,
atualiza o atributo media
para all
e aplica os estilos ao DOM.
Durante o tempo entre a renderização do HTML e a aplicação dos estilos, a página fica parcialmente sem estilo. Quando o navegador usa os estilos, há uma cintilação, que é uma experiência ruim para o usuário e resulta em regressões na mudança de layout cumulativa (CLS, na sigla em inglês).
O uso de CSS crítico em linha, junto com o carregamento de estilo assíncrono, pode melhorar o comportamento de carregamento. A ferramenta critters encontra quais estilos são usados na página, analisando os seletores em uma folha de estilo e os comparando com o HTML. Quando ele encontra uma correspondência, considera os estilos correspondentes como parte do CSS crítico e os coloca inline.
Vejamos um exemplo:
<head> <link rel="stylesheet" href="/styles.css" media="print" onload="this.media='all'"> </head> <body> <section> <button class="primary"></button> </section> </body>
/* styles.css */ section button.primary { /* ... */ } .list { /* ... */ }
No exemplo acima, o Critter vai ler e analisar o conteúdo de styles.css
. Depois disso, ele
vai comparar os dois seletores com o HTML e descobrir que usamos section button.primary
.
Por fim, o Critters vai inserir os estilos correspondentes no <head>
da página, resultando em:
<head> <link rel="stylesheet" href="/styles.css" media="print" onload="this.media='all'"> <style> section button.primary { /* ... */ } </style> </head> <body> <section> <button class="primary"></button> </section> </body>
Depois de incluir o CSS crítico no HTML, você vai notar que a cintilação da página desapareceu:
O inline de CSS crítico agora está disponível no Angular e ativado por padrão na v12. Se você estiver usando a v11,
ative essa opção definindo a propriedade inlineCritical
como true
em angular.json
. Para
ativar esse recurso no Next.js, adicione experimental: { optimizeCss: true }
ao next.config.js
.
Conclusões
Neste post, abordamos algumas das colaborações entre o Chrome e os frameworks da Web. Se você já criou uma estrutura e reconhece alguns dos problemas que abordamos na sua tecnologia, esperamos que nossas descobertas inspirem você a aplicar otimizações de desempenho semelhantes.
Saiba mais sobre as melhorias. Confira uma lista completa do trabalho de otimização que fizemos para as Core Web Vitals na postagem Introdução ao Aurora.