Comment charger CSS de manière asynchrone


92

J'essaie d'éliminer 2 fichiers CSS qui bloquent le rendu sur mon site - ils apparaissent sur Google Page Speed ​​Insights. J'ai suivi différentes méthodes, dont aucune n'a été un succès. Mais, récemment, j'ai trouvé un article sur Thinking Async et quand j'ai appliqué ce code: <script async src="https://third-party.com/resource.js"></script>cela a éliminé le problème.

Cependant, après la publication, la page a perdu le style. Je ne suis pas trop sûr de ce qui se passe car le code fonctionne mais c'est le style après le téléchargement qui ne fonctionne pas. J'apprécierais votre aide avec ceci. Merci


2
Appliquez-vous async aux styles ou scripts? Le style se charge quelque temps après le chargement de la page ou n'apparaît jamais?
kamus

J'ai appliqué l'attribut async aux styles et les ai placés dans l'en-tête.
Paulina994

2
Le truc avec les styles est qu'un re-rendu sera déclenché si vous les chargez tard (par exemple dans le corps), et n'est pas autorisé dans les standards (mais comme les navigateurs sont très indulgents, cela fonctionnera de toute façon). Si le problème est la lenteur des temps de réponse d'un serveur tiers, vous pouvez peut-être simplement les héberger sur votre serveur à la place?
Josef Engelfrost

Réponses:


129

L'astuce pour déclencher un téléchargement de feuille de style asynchrone est d'utiliser un <link>élément et de définir une valeur non valide pour l'attribut media (j'utilise media = "none", mais n'importe quelle valeur fera l'affaire). Lorsqu'une requête multimédia est évaluée à false, le navigateur télécharge toujours la feuille de style, mais il n'attend pas que le contenu soit disponible avant d'afficher la page.

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

Une fois le téléchargement de la feuille de style terminé, l'attribut média doit être défini sur une valeur valide pour que les règles de style soient appliquées au document. L'événement onload est utilisé pour basculer la propriété media sur all:

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

Cette méthode de chargement de CSS fournira un contenu utilisable aux visiteurs beaucoup plus rapidement que l'approche standard. Les CSS critiques peuvent toujours être servies avec l'approche de blocage habituelle (ou vous pouvez les intégrer pour des performances optimales) et les styles non critiques peuvent être progressivement téléchargés et appliqués plus tard dans le processus d'analyse / de rendu.

Cette technique utilise JavaScript, mais vous pouvez prendre en charge les navigateurs non JavaScript en enveloppant les <link>éléments de blocage équivalents dans un <noscript>élément:

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

Vous pouvez voir l'opération sur www.itcha.edu.sv

entrez la description de l'image ici

Source sur http://keithclark.co.uk/


Ok .. Je ne savais pas que vous pouviez référencer les attributs d'élément dans les gestionnaires en mentionnant simplement leur nom. J'ai toujours pensé que «l'événement» était la seule exception.
Teemoh

1
Alors, comment empêchez-vous votre page de clignoter pendant qu'elle restitue tout? De plus, intégrez-vous les styles de shell d'application de base pour que votre page ait une mise en page initiale au lieu de rendre quelque chose dont Lynx serait fier?
Chris Love

2
Semble toujours travailler pour moi. Après cela, je sais que je reçois un avertissement dans PageSpeed ​​Insights pour ce fichier.
AndyWarren

3
cela fonctionne pour moi (08-juillet -2018). Et cela donne un bon score dans la perspicacité de la vitesse de
pages

1
À mon humble avis, la nouvelle réponse de jabachetta utilisant la «précharge» , est probablement la solution préférée maintenant. Si quelqu'un a une raison de penser que cette réponse est toujours préférable, veuillez ajouter un commentaire expliquant pourquoi. Idéalement, un lien vers une ressource qui confirme pourquoi / quand cette approche pourrait encore être préférable. [En supposant que vous utilisez le polyfill pour le support preloadsur Firefox et sur les navigateurs plus anciens - voir le lien dans le premier commentaire de cette réponse].
ToolmakerSteve

109

Mise à jour 2020


La réponse simple (prise en charge complète du navigateur):

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

La réponse documentée (avec préchargement facultatif et secours désactivé par 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échargement et asynchrone combinés:

Si vous avez besoin de CSS préchargé et asynchrone, cette solution combine simplement deux lignes de la réponse documentée ci-dessus, ce qui la rend légèrement plus propre. Mais cela ne fonctionnera pas dans Firefox tant qu'ils ne prendront pas en charge le mot clé de préchargement . Et encore une fois, comme détaillé dans la réponse documentée ci-dessus, le préchargement peut ne pas être réellement bénéfique.

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

Considérations supplémentaires:

Notez que, en général, si vous allez charger CSS de manière asynchrone, il est généralement recommandé d'insérer CSS critique , car CSS est une ressource de blocage de rendu pour une raison .

Nous remercions le groupe filament pour ses nombreuses solutions CSS asynchrones.


3
Il n'y a pas besoin d'utiliser loadCSS, le polyfill suffit: github.com/filamentgroup/loadCSS/blob/master/src
...

1
L'état des spécifications de Preload est finalement [W3C Candidate Recommendation]. ( W3.org/TR/preload/… ) Firefox le prend en charge, mais toujours désactivé par défaut; cliquez sur le lien "largement pris en charge" de la réponse pour obtenir la description "Puis-je utiliser" du drapeau Firefox qui le contrôle.
ToolmakerSteve

1
Réponse mise à jour pour fournir une solution pour une prise en charge complète du navigateur, avec des explications supplémentaires.
jabacchetta le

1
@jabacchetta la mise à jour 2020 est très appréciée, merci. Je cherchais spécifiquement des instructions écrites ces derniers temps où la prise en charge du navigateur est généralement meilleure. Beaucoup de changements en 3 ans sur le web!
Brandito

Apparemment, il vaut mieux onload="this.rel='stylesheet'; this.onload = null". Il est nécessaire de régler this.onloadsur nullpour éviter que cela ne soit appelé deux fois dans certains navigateurs, apparemment.
Flimm

9

Utilisation media="print"etonload

Le groupe filament a récemment (juillet 2019) publié un article donnant sa dernière recommandation sur la façon de charger CSS de manière asynchrone. Même s'ils sont les développeurs de la populaire bibliothèque Javascript loadCSS , ils recommandent en fait cette solution qui ne nécessite pas de bibliothèque Javascript:

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

L'utilisation media="print"indiquera au navigateur de ne pas utiliser cette feuille de style sur les écrans, mais à l'impression. Les navigateurs téléchargent effectivement ces feuilles de style d'impression, mais de manière asynchrone, ce que nous voulons. Nous voulons également que la feuille de style soit utilisée une fois qu'elle est téléchargée, et pour cela, nous définissons onload="this.media='all'; this.onload = null". (Certains navigateurs appelleront onloaddeux fois, pour contourner cela, nous devons définir this.onload = null.) Si vous le souhaitez, vous pouvez ajouter une <noscript>solution de secours pour les rares utilisateurs qui n'ont pas activé Javascript.

L' article original vaut la peine d'être lu, car il est plus détaillé que je ne le suis ici. Cet article sur csswizardry.com vaut également la peine d'être lu.


5

vous pouvez essayer de l'obtenir de plusieurs manières:

1. utilisation media="bogus"et un <link>au pied

<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.Insérer DOM à l'ancienne

<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 vous pouvez essayer des plugins, vous pouvez essayer loadCSS

<script>
  // include loadCSS here...
  function loadCSS( href, before, media ){ ... }
  // load a file
  loadCSS( "path/to/mystylesheet.css" );
</script>

3
L'exemple 2 charge Javascript, mais la question concerne le chargement de CSS. Savez-vous si l'exemple 2 fonctionne également pour CSS, si vous passez de <script>à <style rel=stylesheet>? (Juste curieux. Je vais utiliser loadCSS(c'est-à-dire votre exemple 3) à la place, si j'ai besoin de charger CSS plus tard.)
KajMagnus

5

La fonction ci-dessous créera et ajoutera au document toutes les feuilles de style que vous souhaitez charger de manière asynchrone. (Mais, grâce à Event Listener, il ne le fera qu'après le chargement de toutes les autres ressources de la fenêtre.)

Voir ce qui suit:

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);

1
Y a-t-il des inconvénients de cette méthode si nous la comparons par exemple à l'approche loadCSS? Il semble que le code classique var newStyle = document.createElement("link"); newStyle.rel = "stylesheet"; newStyle.href = "stylesheet.css"; document.getElementsByTagName("head")[0].appendChild(newStyle);à l'intérieur de la <script>balise dans le corps de la page fonctionne parfaitement, même dans les anciens navigateurs comme MSIE8.
TecMan

2
@TecMan oui il y en a. Comme vous pouvez le voir, cette fonction fait son travail sur window.loadévénement. Ainsi, le téléchargement commence lorsque tout est téléchargé. Pas de chance là-bas. Vous avez besoin d'un chargement non bloquant dès que possible pour commencer.
Miloš Đakonović

5

Approches de chargement CSS asynchrone

il existe plusieurs façons de faire en sorte qu'un navigateur charge le CSS de manière asynchrone, bien qu'aucune ne soit aussi simple que vous le pensez.

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

1

Si vous devez charger un lien CSS par programmation et de manière asynchrone:

// 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);
}

0

Si vous avez une politique de sécurité stricte des contenus qui ne permet pas @ vladimir-Salguero de » réponse , vous pouvez utiliser cette (s'il vous plaît prendre note du 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>

Il suffit d' ajouter ce qui suit à votre référence stylesheet: media="none" data-async="true". Voici un exemple:

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

Exemple pour 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>

Je pense que l' asyncattribut est ignoré, car la balise de script n'a pas srcà charger de manière asynchrone ... ou est-ce vraiment utile ici? Pouvez-vous également expliquer un peu plus la valeur à utiliser en tant que nonce?
Philipp

0

Veuillez mettre à jour la réponse car tout ce qui précède ne parvient pas à impressionner les insights google pagespeed maintenant.

Selon Google, c'est ainsi que vous devez implémenter le chargement asynchrone 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>

1
Probablement un faux positif (bogue PageSpeed) si vous utilisez l'approche de préchargement par mot-clé que j'ai mentionnée. github.com/filamentgroup/loadCSS/issues/53
jabacchetta

1
MISE À JOUR: Google a abandonné, puis arrêté, la version 4 de PageSpeed ​​qui avait ce problème - c'était la version pour laquelle ce code asynchrone était recommandé. Bien que je n'ai pas testé dans PageSpeed ​​5 : étant donné la description de la façon dont ils testent maintenant, et leur prise en charge de la balise "préchargement" de "lien", la réponse de jabachetta est probablement une meilleure réponse maintenant, pour Google.
ToolmakerSteve

1
@ToolmakerSteve Oui Cette réponse doit être modérée souvent. Mais ce code fonctionne toujours pour vous obtenir un 100 en vitesse de page.
kaushik gandhi

0

J'ai essayé d'utiliser:

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

Cela fonctionne très bien, mais cela augmente également le décalage de mise en page cumulatif car lorsque nous utilisons rel = "preload", il ne fait que télécharger le CSS, pas appliquer immédiatement.

Exemple lorsque le DOM charge une liste contient des balises ul, li, il y a une puce avant les balises li par défaut, puis CSS appliqué que je supprime ces puces aux styles personnalisés pour la liste. Donc, le changement de mise en page cumulatif se produit ici.

Y a-t-il une solution pour ça?


0

Utilisez rel="preload"pour le télécharger indépendamment, puis utilisez onload="this.rel='stylesheet'"pour l'appliquer à la feuille de style (il as="style"est nécessaire de l'appliquer à la feuille de style sinon onloadcela ne fonctionnera pas)

<link rel="preload" as="style" type="text/css" href="mystyles.css" onload="this.rel='stylesheet'">
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.