Cómo cargar CSS de forma asincrónica

92

Estoy tratando de eliminar 2 archivos CSS que bloquean el procesamiento en mi sitio; aparecen en Google Page Speed ​​Insights. He seguido diferentes métodos, ninguno de los cuales fue un éxito. Pero, recientemente, encontré una publicación sobre Thinking Async y cuando apliqué este código: <script async src="https://third-party.com/resource.js"></script>eliminó el problema.

Sin embargo, después de la publicación, la página perdió el estilo. No estoy muy seguro de lo que está sucediendo porque el código funciona, pero es el estilo después de la carga lo que no funciona. Agradecería su ayuda con esto. Gracias

Paulina994
fuente
2
¿Está aplicando async a estilos o scripts? ¿El estilo se carga algún tiempo después de cargar la página o nunca aparece?
Kamus
Apliqué el atributo async a los estilos y los coloqué en el encabezado.
Paulina994
2
Lo que pasa con los estilos es que se activará una nueva renderización si los carga tarde (por ejemplo, en el cuerpo) y no está permitido en los estándares (pero dado que los navegadores son muy tolerantes, funcionará de todos modos). Si el problema son los tiempos de respuesta lentos de un servidor de terceros, ¿quizás pueda simplemente alojarlos en su servidor?
Josef Engelfrost

Respuestas:

129

El truco para activar una descarga de hoja de estilo asincrónica es usar un <link>elemento y establecer un valor no válido para el atributo de medios (estoy usando media = "none", pero cualquier valor servirá). Cuando una consulta de medios se evalúa como falsa, el navegador aún descargará la hoja de estilo, pero no esperará a que el contenido esté disponible antes de renderizar la página.

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

Una vez que la hoja de estilo ha terminado de descargarse, el atributo de medios debe establecerse en un valor válido para que las reglas de estilo se apliquen al documento. El evento onload se usa para cambiar la propiedad multimedia a all:

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

Este método de carga de CSS entregará contenido utilizable a los visitantes mucho más rápido que el enfoque estándar. El CSS crítico aún se puede servir con el enfoque de bloqueo habitual (o puede integrarlo para obtener el máximo rendimiento) y los estilos no críticos se pueden descargar y aplicar progresivamente más adelante en el proceso de análisis / renderizado.

Esta técnica utiliza JavaScript, pero puede atender a los navegadores que no son de JavaScript envolviendo los <link>elementos de bloqueo equivalentes en un <noscript>elemento:

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

Puedes ver el funcionamiento en www.itcha.edu.sv

ingrese la descripción de la imagen aquí

Fuente en http://keithclark.co.uk/

Vladimir Salguero
fuente
Ok ... No sabía que se pueden hacer referencia a los atributos de los elementos en los controladores con solo mencionar su nombre. Siempre pensé que "evento" es la única excepción.
Teemoh
1
Entonces, ¿cómo evita que su página parpadee mientras se reproduce todo? Además, ¿tiene estilos de shell de aplicaciones centrales en línea para que su página tenga un diseño inicial en lugar de representar algo de lo que Lynx estaría orgulloso?
Chris Love
2
Parece que todavía funciona para mí. Después de hacer esto, sé que ya recibo una advertencia en PageSpeed ​​Insights para este archivo.
AndyWarren
3
esto está funcionando para mí (08-julio -2018). Y da una buena puntuación en la información de la velocidad de la página
abilash er
1
En mi humilde opinión, la respuesta más reciente de jabachetta que usa "precarga" , es probablemente la solución preferida ahora. Si alguien tiene una razón para pensar que esta respuesta sigue siendo preferible, agregue un comentario que explique por qué. Lo ideal sería vincularlo a un recurso que confirme por qué / cuándo este enfoque aún podría ser preferible. [Suponiendo que use polyfill para admitir preloaden Firefox y en navegadores más antiguos, vea el enlace en el primer comentario de esa respuesta].
ToolmakerSteve
109

Actualización 2020


La respuesta simple (compatibilidad total con el navegador):

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

La respuesta documentada (con precarga opcional y respaldo con script desactivado):

 <!-- 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>

Precarga y asincrónica combinados:

Si necesita CSS precargado y asíncrono, esta solución simplemente combina dos líneas de la respuesta documentada anterior, haciéndola un poco más limpia. Pero esto no funcionará en Firefox hasta que admitan la palabra clave de precarga . Y nuevamente, como se detalla en la respuesta documentada anterior, la precarga puede no ser realmente beneficiosa.

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

Consideraciones adicionales:

Tenga en cuenta que, en general, si va a cargar CSS de forma asincrónica, generalmente se recomienda que inserte CSS crítico en línea , ya que CSS es un recurso de bloqueo de representación por una razón .

Crédito al grupo de filamentos por sus muchas soluciones CSS asíncronas.

jabacchetta
fuente
3
No es necesario usar loadCSS, el polyfill es suficiente: github.com/filamentgroup/loadCSS/blob/master/src/…
nathan
1
El estado de especificación de la precarga es finalmente [Recomendación candidata del W3C]. ( W3.org/TR/preload/… ) Firefox lo admite, pero aún está deshabilitado por defecto; haga clic en el enlace "ampliamente admitido" de la respuesta para ver la descripción de "Puedo usar" de la bandera de Firefox que lo controla.
ToolmakerSteve
1
Respuesta actualizada para proporcionar una solución para la compatibilidad total con el navegador, junto con una explicación adicional.
jabacchetta
1
@jabacchetta, la actualización de 2020 es muy apreciada, gracias. Estaba buscando específicamente instrucciones escritas en tiempos recientes donde el soporte del navegador es generalmente mejor. ¡Muchos cambios en 3 años en la web!
Brandito
Aparentemente, es mejor onload="this.rel='stylesheet'; this.onload = null". Es necesario configurarlo this.onloadpara nullevitar que esto se llame dos veces en algunos navegadores, aparentemente.
Flimm
9

Usando media="print"yonload

El grupo de filamentos publicó recientemente (julio de 2019) un artículo con su última recomendación sobre cómo cargar CSS de forma asincrónica. Aunque son los desarrolladores de la popular biblioteca de Javascript loadCSS , en realidad recomiendan esta solución que no requiere una biblioteca de Javascript:

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

El uso media="print"le indicará al navegador que no utilice esta hoja de estilo en las pantallas, sino en la impresión. Los navegadores descargan estas hojas de estilo de impresión, pero de forma asincrónica, que es lo que queremos. También queremos que la hoja de estilo se use una vez que se descargue, y para eso configuramos onload="this.media='all'; this.onload = null". (Algunos navegadores llamarán onloaddos veces, para solucionarlo, debemos configurarlo this.onload = null). Si lo desea, puede agregar un <noscript>respaldo para los pocos usuarios que no tienen Javascript habilitado.

El artículo original es digno de una lectura, ya que entra en más detalles de lo que yo estoy aquí. También vale la pena leer este artículo sobre csswizardry.com .

Flimm
fuente
5

puedes intentar conseguirlo de muchas formas:

1.Uso media="bogus"y <link>a al pie

<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.Insertar DOM a la antigua usanza

<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.Si puede probar complementos, puede probar loadCSS

<script>
  // include loadCSS here...
  function loadCSS( href, before, media ){ ... }
  // load a file
  loadCSS( "path/to/mystylesheet.css" );
</script>
Kamus
fuente
3
El ejemplo 2 carga Javascript, pero la pregunta es sobre cómo cargar CSS. ¿Sabe si el ejemplo 2 funciona también para CSS, si cambia de <script>a <style rel=stylesheet>? (Sólo por curiosidad, voy a usar. loadCSS(Es decir, el ejemplo 3) En cambio, si necesito CSS carga más tarde.)
KajMagnus
5

La función siguiente creará y agregará al documento todas las hojas de estilo que desee cargar de forma asincrónica. (Pero, gracias a Event Listener, solo lo hará después de que se hayan cargado todos los demás recursos de la ventana).

Vea lo siguiente:

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
fuente
1
¿Hay algún inconveniente de este método si lo comparamos, digamos, con el enfoque loadCSS? Parece que el código clásico var newStyle = document.createElement("link"); newStyle.rel = "stylesheet"; newStyle.href = "stylesheet.css"; document.getElementsByTagName("head")[0].appendChild(newStyle);dentro de la <script>etiqueta en el cuerpo de la página hace su trabajo de manera excelente, incluso en navegadores antiguos como MSIE8.
TecMan
2
@TecMan sí los hay. Como puede ver, esta función hace su trabajo en window.loadcaso de evento. Entonces, la descarga comienza cuando se descarga todo. No hubo suerte ahí. Necesita cargar sin bloqueo lo antes posible para comenzar.
Miloš Đakonović
5

Enfoques de carga de CSS asíncrono

Hay varias formas de hacer que un navegador cargue CSS de forma asincrónica, aunque ninguna es tan simple como cabría esperar.

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

Si necesita cargar un enlace CSS mediante programación y asincrónica:

// 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
fuente
0

Si tiene una política de seguridad de contenido estricta que no permite la respuesta de @ vladimir-salguero , puede usar esto (tome nota del 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>

Sólo tiene que añadir lo siguiente a su referencia de estilo: media="none" data-async="true". He aquí un ejemplo:

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

Ejemplo de 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
fuente
Creo que el asyncatributo se ignora, ya que la etiqueta del script no tiene srcque cargar de forma asincrónica ... ¿o es realmente útil aquí? ¿También puede explicar un poco más qué valor usar como nonce?
Philipp
0

Tenga cuidado de actualizar la respuesta, ya que todo lo anterior no logra impresionar a Google Pagespeed Insights ahora.

Según Google, así es como debe implementar la carga asíncrona 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
fuente
1
Probablemente un falso positivo (error de PageSpeed) si está utilizando el enfoque de palabra clave de precarga que mencioné. github.com/filamentgroup/loadCSS/issues/53
jabacchetta
1
ACTUALIZACIÓN: Google ha desaprobado y luego cerrado la versión 4 de PageSpeed ​​que tenía este problema; esa era la versión para la que se recomendaba este código asíncrono. Aunque no he probado en PageSpeed ​​5 : dada la descripción de cómo prueban ahora, y su soporte para la etiqueta "preload" de "link", la respuesta de jabachetta es probablemente una mejor respuesta ahora, para Google.
ToolmakerSteve
1
@ToolmakerSteve Sí Esta respuesta debe ser moderada con frecuencia. Pero este código todavía funciona para obtener 100 en Page Speed.
kaushik gandhi
0

He intentado usar:

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

Funciona bien, pero también aumenta el cambio de diseño acumulativo porque cuando usamos rel = "preload", simplemente descarga css, no aplica inmediatamente.

Por ejemplo, cuando el DOM carga una lista que contiene etiquetas ul, li, hay viñetas antes de las etiquetas li de forma predeterminada, luego CSS aplicó que elimino estas viñetas a estilos personalizados para la lista. Entonces, el cambio de diseño acumulativo está sucediendo aquí.

¿Hay alguna solución para eso?

n2lose
fuente
0

Úselo rel="preload"para descargarlo de forma independiente, luego use onload="this.rel='stylesheet'"para aplicarlo a la hoja de estilo ( as="style"es necesario aplicarlo a la hoja de estilo, de lo contrario onloadno funcionará)

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