Recursos em linha em frameworks JavaScript

Melhorar a Maior exibição de conteúdo no ecossistema JavaScript.

Como parte do projeto Aurora, o Google está trabalhando com frameworks da Web conhecidos para garantir que eles funcionem bem de acordo com as Core Web Vitals. O Angular e o Next.js já têm fontes inline, o que é explicado na primeira parte deste artigo. A segunda otimização que abordaremos é o in-line de CSS essencial, que agora está ativado por padrão na CLI do Angular e tem uma implementação em andamento no Nuxt.js.

Fonte em linha

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 í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 completamente 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 mencionada 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 font-face se refere a um arquivo externo hospedado em fonts.gstatic.com. Ao carregar o aplicativo, primeiro o navegador precisa fazer o download da folha de estilo original referenciada no cabeçalho.

Uma imagem mostrando como o site precisa fazer uma solicitação ao servidor e fazer o download da folha de estilo externa
Primeiro, o site carrega a folha de estilo da fonte.

Em seguida, o navegador faz o download do arquivo woff2 e, por fim, pode prosseguir com a renderização do aplicativo.

Uma imagem mostrando as duas solicitações feitas, uma para a folha de estilo de fonte e outra para o arquivo de fontes.
Em seguida, é feita uma solicitação para carregar a fonte.

Uma oportunidade de otimização é fazer o download da folha de estilo inicial no momento da criação e inseri-la inline em index.html. Isso pula um retorno inteiro para a CDN no momento da execução, reduzindo o tempo de bloqueio.

Ao criar o aplicativo, uma solicitação é enviada para a CDN. A folha de estilo é buscada e 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>

Fontes inline agora estão disponíveis no Next.js e no Angular

Quando os desenvolvedores de framework implementam a otimização nas ferramentas subjacentes, eles facilitam a ativação por aplicativos novos e atuais, trazendo a melhoria para todo o ecossistema.

Essa melhoria é ativada por padrão no Next.js v10.2 e no Angular v11. Os dois são compatíveis com fontes inline do Google e Adobe. O Angular espera introduzir a última versão na v12.2.

Você pode encontrar a implementação de fontes in-line no Next.js no GitHub e conferir o vídeo que explica essa otimização no contexto do Angular (em inglês).

In-line de CSSs essenciais

Outra melhoria envolve melhorar as métricas de Primeira exibição de conteúdo (FCP, na sigla em inglês) e de Maior exibição de conteúdo (LCP, na sigla em inglês) com a inserção in-line de CSSs essenciais. O CSS essencial de uma página inclui todos os estilos usados na renderização inicial. Para saber mais sobre o tópico, consulte Adiar CSS não essencial.

Observamos que muitos aplicativos carregam estilos de forma síncrona, o que bloqueia a renderização de aplicativos. 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'">

Essa prática, no entanto, pode causar oscilações de conteúdo sem estilo.

A página parece piscar à medida que os estilos são carregados.

O vídeo acima mostra a renderização de uma página, que carrega os estilos de forma assíncrona. Isso acontece porque o navegador começa a fazer o download dos estilos e depois renderiza o HTML seguinte. Depois que o navegador faz o download dos estilos, ele aciona o evento onload do elemento do link, atualizando 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 não tem um estilo. Quando o navegador usa os estilos, notamos oscilações, o que é uma experiência ruim para o usuário e resulta em regressões na Mudança de layout cumulativa (CLS).

O elemento inline essencial do CSS, assim como o carregamento de estilo assíncrono, pode melhorar o comportamento de carregamento. A ferramenta critters (link em inglês) descobre quais estilos são usados na página analisando os seletores em uma folha de estilo e fazendo a correspondência deles com o HTML. Quando encontra uma correspondência, ele considera os estilos correspondentes como parte do CSS essencial e os in-line.

Vejamos um exemplo:

O que não fazer
<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 {
  /* ... */
}

Exemplo antes de inserir inline.

No exemplo acima, as criaturas leem e analisam o conteúdo de styles.css. Depois disso, a correspondência entre os dois seletores no HTML e o HTML é descoberto em que usamos section button.primary. Por fim, as criaturas inserem os estilos correspondentes no <head> da página, o que resulta em:

O que fazer
<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>

Exemplo após a inserção in-line.

Depois da inserção in-line do CSS essencial no HTML, você verá que a oscilação da página desapareceu:

O carregamento da página após a inserção in-line de CSS.

O in-line CSS essencial agora está disponível no Angular e é ativado por padrão na v12. Se você estiver usando a v11, ative-a definindo a propriedade inlineCritical como true no angular.json. Para ativar esse recurso no Next.js, adicione experimental: { optimizeCss: true } ao next.config.js.

Conclusões

Nesta postagem, falamos sobre a colaboração entre o Chrome e as estruturas da Web. Se você é um autor de framework 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. Você pode encontrar uma lista abrangente do trabalho de otimização que estamos fazendo para as Core Web Vitals na postagem Apresentação do Aurora.