Como carregar CSS de forma assíncrona

94

Estou tentando eliminar 2 arquivos CSS que estão bloqueando a renderização em meu site - eles aparecem no Google Page Speed ​​Insights. Tenho seguido métodos diferentes, nenhum dos quais foi um sucesso. Mas, recentemente, eu encontrei um post sobre Thinking Async e quando apliquei esse código: <script async src="https://third-party.com/resource.js"></script>ele eliminou o problema.

No entanto, após a publicação, a página perdeu o estilo. Não tenho certeza do que está acontecendo porque o código funciona, mas é o estilo após o upload que não funciona. Agradeceria sua ajuda com isso. obrigado

Paulina994
fonte
2
Você está aplicando assíncrono a estilos ou scripts? O estilo carrega algum tempo depois que você carrega a página ou nunca aparece?
Kamus
Eu apliquei o atributo async aos estilos e os coloquei no cabeçalho.
Paulina994
2
O problema com os estilos é que uma re-renderização será acionada se você carregá-los tarde (por exemplo, no corpo), e não é permitido nos padrões (mas como os navegadores são muito complacentes, funcionará de qualquer maneira). Se o problema for tempos de resposta lentos de um servidor de terceiros, talvez você possa simplesmente hospedá-los em seu servidor?
Josef Engelfrost

Respostas:

131

O truque para acionar um download de folha de estilo assíncrono é usar um <link>elemento e definir um valor inválido para o atributo media (estou usando media = "none", mas qualquer valor servirá). Quando uma consulta de mídia é avaliada como falsa, o navegador ainda baixa a folha de estilo, mas não espera que o conteúdo esteja disponível antes de renderizar a página.

<link rel="stylesheet" href="css.css" media="none">

Assim que o download da folha de estilo terminar, o atributo de mídia deve ser definido com um valor válido para que as regras de estilo sejam aplicadas ao documento. O evento onload é usado para mudar a propriedade de mídia para todos:

<link rel="stylesheet" href="css.css" media="none" onload="if(media!='all')media='all'">

Este método de carregamento de CSS entregará conteúdo utilizável aos visitantes muito mais rápido do que a abordagem padrão. CSS crítico ainda pode ser servido com a abordagem usual de bloqueio (ou você pode embuti-lo para melhor desempenho) e estilos não críticos podem ser baixados progressivamente e aplicados posteriormente no processo de análise / renderização.

Essa técnica usa JavaScript, mas você pode atender a navegadores não JavaScript envolvendo os <link>elementos de bloqueio equivalentes em um <noscript>elemento:

<link rel="stylesheet" href="css.css" media="none" onload="if(media!='all')media='all'"><noscript><link rel="stylesheet" href="css.css"></noscript>

Você pode ver a operação em www.itcha.edu.sv

insira a descrição da imagem aqui

Fonte em http://keithclark.co.uk/

Vladimir Salguero
fonte
Ok .. Eu não sabia que você pode referenciar atributos de elemento nos manipuladores on apenas mencionando seus nomes. Sempre pensei que 'evento' fosse a única exceção.
Teemoh
1
Então, como você evita que sua página pisque enquanto processa tudo novamente? Além disso, você inline estilos de shell de aplicativo principal para que sua página tenha um layout inicial em vez de renderizar algo de que o Lynx ficaria orgulhoso?
Chris Love
2
Parece que ainda funciona para mim. Depois de fazer isso, sei que mais receberei um aviso no PageSpeed ​​Insights para este arquivo.
AndyWarren
3
isso está funcionando para mim (08 de julho -2018). E dando uma boa pontuação no insight de
velocidade de página
1
IMHO, a resposta mais recente de jabachetta usando "pré-carga" , é provavelmente a solução preferida agora. Se alguém tiver um motivo para pensar que esta resposta ainda é preferível, adicione um comentário explicando o motivo. O ideal é vincular a um recurso que confirma por que / quando essa abordagem ainda pode ser preferível. [Supondo que você use o polyfill para oferecer suporte preloadno Firefox e em navegadores mais antigos - veja o link no primeiro comentário dessa resposta].
ToolmakerSteve
118

Atualização de 2020


A resposta simples (suporte completo ao navegador):

<link rel="stylesheet" href="style.css" media="print" onload="this.media='all'">

A resposta documentada (com pré-carregamento opcional e fallback desativado por script):

 <!-- Optional, if we want the stylesheet to get preloaded. Note that this line causes stylesheet to get downloaded, but not applied to the page. Use strategically — while preloading will push this resource up the priority list, it may cause more important resources to be pushed down the priority list. This may not be the desired effect for non-critical CSS, depending on other resources your app needs. -->
 <link rel="preload" href="style.css" as="style">

 <!-- Media type (print) doesn't match the current environment, so browser decides it's not that important and loads the stylesheet asynchronously (without delaying page rendering). On load, we change media type so that the stylesheet gets applied to screens. -->
 <link rel="stylesheet" href="style.css" media="print" onload="this.media='all'">

 <!-- Fallback that only gets inserted when JavaScript is disabled, in which case we can't load CSS asynchronously. -->
 <noscript><link rel="stylesheet" href="style.css"></noscript>

Pré-carregamento e assíncrono combinados:

Se você precisa de CSS pré-carregado e assíncrono, esta solução simplesmente combina duas linhas da resposta documentada acima, tornando-a um pouco mais limpa. Mas isso não funcionará no Firefox até que eles suportem a palavra-chave preload . E, novamente, conforme detalhado na resposta documentada acima, o pré-carregamento pode não ser benéfico.

<link href="style.css" rel="preload" as="style" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="style.css"></noscript>

Considerações adicionais:

Observe que, em geral, se você for carregar CSS de forma assíncrona, é geralmente recomendado que você inline CSS crítico , já que CSS é um recurso de bloqueio de renderização por um motivo .

Crédito ao grupo de filamentos por suas muitas soluções CSS assíncronas.

Jabacchetta
fonte
3
Não há necessidade de usar loadCSS, o polyfill é o suficiente: github.com/filamentgroup/loadCSS/blob/master/src/…
nathan
1
O status da especificação do Preload é finalmente [W3C Candidate Recommendation]. ( W3.org/TR/preload/… ) O Firefox oferece suporte, mas ainda desabilitado por padrão; clique no link "amplamente suportado" da resposta para a descrição "Posso usar" do sinalizador do Firefox que o controla.
Toolmaker Steve
1
Resposta atualizada para fornecer solução para suporte total ao navegador, junto com explicação adicional.
jabacchetta
1
@jabacchetta a atualização de 2020 é muito apreciada, obrigado. Eu estava procurando especificamente por instruções escritas recentemente, onde o suporte ao navegador é geralmente melhor. Muitas mudanças ao longo de 3 anos na web!
Brandito
2
Aparentemente, é melhor onload="this.rel='stylesheet'; this.onload = null". É necessário configurar this.onloadpara nullevitar que isso seja chamado duas vezes em alguns navegadores, aparentemente.
Flimm de
10

Usando media="print"eonload

O grupo de filamentos publicou recentemente (julho de 2019) um artigo com sua recomendação mais recente sobre como carregar CSS de forma assíncrona. Mesmo sendo eles os desenvolvedores da popular biblioteca Javascript loadCSS , eles realmente recomendam esta solução que não requer uma biblioteca Javascript:

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

Usar media="print"indicará ao navegador para não usar esta folha de estilo nas telas, mas na impressão. Os navegadores realmente baixam essas folhas de estilo de impressão, mas de forma assíncrona, que é o que queremos. Também queremos que a folha de estilo seja usada assim que for baixada, e para isso definimos onload="this.media='all'; this.onload = null". (Alguns navegadores chamarão onloadduas vezes; para contornar isso, precisamos definir this.onload = null.) Se desejar, você pode adicionar um <noscript>substituto para os raros usuários que não têm o Javascript habilitado.

O artigo original vale a pena ler, pois ele entra em mais detalhes do que eu estou aqui. Este artigo em csswizardry.com também vale a pena ler.

Flimm
fonte
5

você pode tentar obtê-lo de várias maneiras:

1. Usando media="bogus"e <link>ao pé

<head>
    <!-- unimportant nonsense -->
    <link rel="stylesheet" href="style.css" media="bogus">
</head>
<body>
    <!-- other unimportant nonsense, such as content -->
    <link rel="stylesheet" href="style.css">
</body>

2. Inserindo DOM da maneira antiga

<script type="text/javascript">
(function(){
  var bsa = document.createElement('script');
     bsa.type = 'text/javascript';
     bsa.async = true;
     bsa.src = 'https://s3.buysellads.com/ac/bsa.js';
  (document.getElementsByTagName('head')[0]||document.getElementsByTagName('body')[0]).appendChild(bsa);
})();
</script>

3.se você pode tentar plugins, você pode tentar loadCSS

<script>
  // include loadCSS here...
  function loadCSS( href, before, media ){ ... }
  // load a file
  loadCSS( "path/to/mystylesheet.css" );
</script>
Kamus
fonte
3
O exemplo 2 carrega Javascript, mas a questão é sobre como carregar CSS. Você sabe se o exemplo 2 também funciona para CSS, se você mudar de <script>para <style rel=stylesheet>? (Só por curiosidade. Vou usar loadCSS(ou seja, seu exemplo 3) em vez disso, se precisar carregar o CSS mais tarde.)
KajMagnus
5

A função abaixo irá criar e adicionar ao documento todas as folhas de estilo que você deseja carregar de forma assíncrona. (Mas, graças ao Event Listener, isso só acontecerá depois que todos os outros recursos da janela forem carregados.)

Veja o seguinte:

function loadAsyncStyleSheets() {

    var asyncStyleSheets = [
    '/stylesheets/async-stylesheet-1.css',
    '/stylesheets/async-stylesheet-2.css'
    ];

    for (var i = 0; i < asyncStyleSheets.length; i++) {
        var link = document.createElement('link');
        link.setAttribute('rel', 'stylesheet');
        link.setAttribute('href', asyncStyleSheets[i]);
        document.head.appendChild(link);
    }
}

window.addEventListener('load', loadAsyncStyleSheets, false);
Rounin
fonte
1
Há alguma desvantagem desse método se o compararmos, digamos, com a abordagem loadCSS? Parece que o código clássico var newStyle = document.createElement("link"); newStyle.rel = "stylesheet"; newStyle.href = "stylesheet.css"; document.getElementsByTagName("head")[0].appendChild(newStyle);dentro da <script>tag no corpo da página faz seu trabalho de maneira excelente - mesmo em navegadores antigos como o MSIE8.
TecMan
2
@TecMan sim, existem. Como você pode ver, esta função faz seu trabalho no window.loadevento. Portanto, o download começa quando tudo é baixado. Sem sorte aí. Você precisa não bloquear o carregamento o mais rápido possível para começar.
Miloš Đakonović
5

Abordagens de carregamento assíncrono de CSS

Existem várias maneiras de fazer um navegador carregar CSS de forma assíncrona, embora nenhuma seja tão simples quanto você pode esperar.

<link rel="preload" href="mystyles.css" as="style" onload="this.rel='stylesheet'">
virender nehra
fonte
1

Se você precisar carregar um link CSS de maneira programática e assíncrona:

// https://www.filamentgroup.com/lab/load-css-simpler/
function loadCSS(href, position) {
  const link = document.createElement('link');
  link.media = 'print';
  link.rel = 'stylesheet';
  link.href = href;
  link.onload = () => { link.media = 'all'; };
  position.parentNode.insertBefore(link, position);
}
Olivier Tassinari
fonte
0

Se você tem uma política de segurança de conteúdo rígida que não permite a resposta de @vladimir-salguero , você pode usar isto (observe o script ):nonce

<script nonce="(your nonce)" async>
$(document).ready(function() {
    $('link[media="none"]').each(function(a, t) {
        var n = $(this).attr("data-async"),
            i = $(this);
        void 0 !== n && !1 !== n && ("true" == n || n) && i.attr("media", "all")
    })
});
</script>

Basta adicionar o seguinte para sua referência de estilo: media="none" data-async="true". Aqui está um exemplo:

<link rel="stylesheet" href="../path/script.js" media="none" data-async="true" />

Exemplo para jQuery:

<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css" type="text/css" media="none" data-async="true" crossorigin="anonymous" /><noscript><link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css" type="text/css" /></noscript>
Bradley
fonte
Acho que o asyncatributo é ignorado, pois a tag de script não tem um srcpara carregar de forma assíncrona ... ou é realmente útil aqui? Além disso, você pode elaborar um pouco mais sobre qual valor usar como um nonce?
Philipp,
0

Por favor, atualize a resposta, pois todas as opções acima não impressionam os insights do google pagepeed agora.

De acordo com o Google, é assim que você deve implementar o carregamento assíncrono de Css

 < noscript id="deferred-styles" >
        < link rel="stylesheet" type="text/css" href="small.css"/ >
    < /noscript >

<script>
  var loadDeferredStyles = function() {
    var addStylesNode = document.getElementById("deferred-styles");
    var replacement = document.createElement("div");
    replacement.innerHTML = addStylesNode.textContent;
    document.body.appendChild(replacement)
    addStylesNode.parentElement.removeChild(addStylesNode);
  };
  var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
      window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
  if (raf) raf(function() { window.setTimeout(loadDeferredStyles, 0); });
  else window.addEventListener('load', loadDeferredStyles);
</script>
Kaushik Gandhi
fonte
1
Provavelmente um falso positivo (bug PageSpeed) se você estiver usando a abordagem de palavra-chave de pré-carregamento que mencionei. github.com/filamentgroup/loadCSS/issues/53
jabacchetta
1
ATUALIZAÇÃO: o Google suspendeu e, em seguida, desligou o PageSpeed ​​versão 4 que apresentava esse problema - essa era a versão para a qual esse código assíncrono foi recomendado. Embora eu não tenha testado no PageSpeed ​​5 : dada a descrição de como eles testam agora, e seu suporte para a tag "pré-carregamento" do "link", a resposta de jabachetta é provavelmente uma resposta melhor agora, para o Google.
ToolmakerSteve
1
@ToolmakerSteve Sim Esta resposta precisa ser moderada com freqüência. Mas esse código ainda funciona para que você obtenha 100 no Page Speed.
Kaushik Gandhi
0

Eu tentei usar:

<link rel="preload stylesheet" href="mystyles.css" as="style">

Funciona muito bem, mas também aumenta a mudança cumulativa de layout porque quando usamos rel = "preload", ele apenas baixa o css, não aplica imediatamente.

Exemplo quando o DOM carrega uma lista que contém as tags ul, li, há marcadores antes das tags li por padrão, então o CSS aplica que eu remova esses marcadores para estilos personalizados para listagem. Portanto, a mudança cumulativa de layout está acontecendo aqui.

Há alguma solução para isso?

n2perder
fonte
0

Use rel="preload"para fazer o download independentemente e, em seguida, use onload="this.rel='stylesheet'"para aplicá-lo à folha de estilo ( as="style"é necessário aplicá-lo à folha de estilo, caso contrário onload, não funcionará)

<link rel="preload" as="style" type="text/css" href="mystyles.css" onload="this.rel='stylesheet'">
Shammi Hans
fonte