CSS: Carregando CSS de forma assíncrona

Uma maneira simples de melhorar performance

Uma das coisas mais impactantes que podemos fazer para melhorar o desempenho e a resiliência de uma página web é carregar o CSS de uma maneira que não atrase a renderização da página. Isso ocorre porque, por padrão, os navegadores carregam o CSS externo de maneira síncrona — parando toda a renderização da página enquanto o CSS é baixado e analisado — e ambos acarretam possíveis atrasos. Obviamente, pelo menos uma parte do CSS do site deve ser carregada antes que a página comece a ser renderizada e, para obter imediatamente esse CSS inicial para o navegador, recomendamos colocá-lo na página em si (inline css ou HTTP2 server-pushing). Para sites com uma quantidade total pequena, isso por si só pode ser suficiente, mas se o CSS for grande (digamos, maior que 15 a 20kb), essa técnica pode ajudar o desempenho e dividí-lo por prioridade. Uma vez dividido, o CSS menos crítico deve ser carregado em segundo plano — a.k.a. de forma assíncrona . Neste artigo, pretendo descrever a maneira preferida de fazer isso nos dias de hoje, que já existe há anos!

Há várias maneiras de fazer com que o CSS seja carregado de forma assíncrona, mas nenhuma é tão intuitiva quanto você espera. Ao contrário dos elementos script, não há nenhum atributo async ou defer que podemos simplesmente aplicar a um elemento link, portanto, há anos mantemos o projeto loadCSS para facilitar o processo de carregamento do CSS assíncrono. Recentemente, porém, os navegadores padronizaram seu comportamento de carregamento de CSS, de modo que um script dedicado, como o loadCSS, para lidar com suas pequenas diferenças provavelmente não será mais necessário.

O Código

Hoje, armados com um pouco de conhecimento de como o navegador lida com vários atributos de elementos link, podemos obter o efeito de carregar CSS de forma assíncrona com uma pequena linha de HTML. Aqui está, a maneira mais simples de carregar uma folha de estilo de forma assíncrona:

<link 
rel="stylesheet"
href="/path/to/my.css"
media="print"
onload="this.media='all'"
/>

Entendendo suas partes

Essa linha de HTML é concisa, mas não é muito intuitiva, então vamos detalhar o que está acontecendo aqui.

Para começar, o atributo media do elemento link está definido para print. “print” é uma tipo de mídia, (media type) que diz: “aplique as regras desta folha de estilo para mídias baseada em impressão” ou, em outras palavras, aplique-as quando o usuário tentar imprimir a página. Francamente, queremos que nossa folha de estilo seja aplicada a todas as mídias (especialmente telas, screens) e não apenas a impressão, mas ao declarar um tipo de mídia que não corresponde ao ambiente atual, podemos obter um efeito interessante e útil: o navegador carregará a folha de estilo sem atrasar a renderização da página, de forma assíncrona! Isso é útil, mas não é tudo que queremos. Também queremos que o CSS se aplique ao ambiente de tela quando ele for carregado. Para isso, podemos usar o atributo onload para definir o tipo de mídia para all quando terminar o carregamento.

O “rel=preload” não pode fazer isso também?

Sim, da mesma forma! Nos últimos dois anos, usamos link[rel=preload] (ao invés de rel=stylesheet) para obter um padrão semelhante ao anterior (alternando o atributo rel uma vez carregado, ao invés do atributo media, respectivamente). Ainda funciona bem usar essa abordagem, no entanto, há algumas desvantagens a considerar ao usar preload. Primeiro, o suporte de navegadores para preload ainda não é excelente, portanto, um polyfill (como o que o loadCSS oferece) é necessário se você quiser confiar nesse atributo para buscar e aplicar uma folha de estilo nos navegadores. Mais importante ainda, preload busca arquivos muito cedo, early loading, com a prioridade mais alta, highest priority, potencialmente desprivilegiando outros downloads importantes, e isso pode ser uma prioridade maior do que você realmente precisa para CSS não crítico.

Felizmente, se você deseja a busca de alta prioridade que rel=preloadfornece (nos navegadores que a suportam), você pode combiná-la com o padrão acima, assim:

<link 
rel="preload"
href="/path/to/my.css"
as="style"
/>
<link
rel="stylesheet"
href="/path/to/my.css"
media="print"
onload="this.media='all'"
/>

Dada a natureza simples e declarativa do código acima, nós escolheríamos isso ao invés de um polyfill, então a abordagem de alternância de mídia de impressão é nossa preferência novamente.

Por que não utilizar um atributo de tipo de mídia falso?

Você que tem lido nossos artigos sobre isso nos últimos anos pode lembrar que usamos valores de atributo de mídia como “only X” para obter o mesmo efeito que “print”, fornecendo um valor que não corresponde a nenhum ambiente, como um tipo de mídia “X” sem sentido. Quando os navegadores se deparam com tipos de mídia que não correspondem a nenhum ambiente, eles atualmente os tratam da mesma forma — eles carregam o arquivo de qualquer maneira. Dito isso, algumas equipes de navegadores estão começando a considerar a diferenciação entre tipos de mídia não correspondentes e aqueles que não são válidos (ou que não são reconhecidos pelo navegador) e que potencialmente, podem não solicitam os arquivos vinculados que usam tipos de mídia inválidos. Isso quebraria muitas implementações de carregamento de CSS existentes, portanto, não é provável, mas, por questões de segurança, recomendamos o uso de um tipo válido e compatível que não corresponda ao ambiente atual, como “print”.

Você pode não precisar de loadCSS

Continuamos a manter o loadCSS e achar que é útil em algumas situações, como quando você quiser programaticamente buscar um arquivo CSS em JavaScript, como: loadCSS(“/path/to/my.css”). Se você já estiver usando loadCSS ou seu rel=preload padrão de polyfill, não precisará alterar nada. Internamente, usamos a mesma mecânica descrita neste artigo.

Porém, mais e mais estamos descobrindo que a abordagem simples em HTML pode ser tudo o que você precisa.

E simples é geralmente melhor.

Obrigado pela leitura

Perguntas? Nos adicione no Twitter!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store