Jak ładować CSS asynchronicznie

92

Próbuję wyeliminować 2 pliki CSS, które blokują renderowanie w mojej witrynie - pojawiają się one w Google Page Speed ​​Insights. Stosowałem różne metody, z których żadna nie okazała się sukcesem. Ale ostatnio znalazłem post o Thinking Async i kiedy zastosowałem ten kod: <script async src="https://third-party.com/resource.js"></script>wyeliminował problem.

Jednak po opublikowaniu strona straciła stylistykę. Nie jestem pewien, co się dzieje, ponieważ kod działa, ale to stylizacja po przesłaniu nie działa. Byłbym wdzięczny za twoją pomoc. Dzięki

Paulina994
źródło
2
Czy stosujesz asynchroniczność do stylów lub skryptów? Styl ładuje się jakiś czas po załadowaniu strony lub nigdy się nie pojawia?
kamus
Zastosowałem atrybut async do stylów i umieściłem je w nagłówku.
Paulina994
2
Problem ze stylami polega na tym, że ponowne renderowanie zostanie uruchomione, jeśli załadujesz je późno (np. W treści) i nie jest dozwolone w standardach (ale ponieważ przeglądarki są bardzo wyrozumiałe, i tak będzie działać). Jeśli problemem są długie czasy odpowiedzi z serwera innej firmy, być może możesz po prostu hostować je na swoim serwerze?
Josef Engelfrost

Odpowiedzi:

129

Sztuczka wyzwalania asynchronicznego pobierania arkusza stylów polega na użyciu <link>elementu i ustawieniu nieprawidłowej wartości dla atrybutu media (używam media = "none", ale każda wartość wystarczy). Gdy zapytanie o media ma wartość false, przeglądarka nadal pobierze arkusz stylów, ale nie będzie czekać, aż zawartość będzie dostępna przed renderowaniem strony.

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

Po zakończeniu pobierania arkusza stylów atrybut media musi być ustawiony na prawidłową wartość, aby reguły stylu zostały zastosowane do dokumentu. Zdarzenie onload służy do przełączania właściwości media na all:

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

Ta metoda ładowania CSS zapewni odwiedzającym użyteczne treści znacznie szybciej niż standardowe podejście. Krytyczny CSS może być nadal obsługiwany przy użyciu zwykłego podejścia blokującego (lub można go wbudować w celu uzyskania najwyższej wydajności), a style niekrytyczne można stopniowo pobierać i stosować później w procesie analizowania / renderowania.

Ta technika wykorzystuje JavaScript, ale możesz obsługiwać przeglądarki inne niż JavaScript, opakowując równoważne <link>elementy blokujące w <noscript>element:

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

Operację można zobaczyć na stronie www.itcha.edu.sv

wprowadź opis obrazu tutaj

Źródło w http://keithclark.co.uk/

Vladimir Salguero
źródło
Ok .. Nie wiedziałem, że możesz odwoływać się do atrybutów elementu w procedurach obsługi, po prostu podając ich nazwę. Zawsze myślałem, że „wydarzenie” jest jedynym wyjątkiem.
Teemoh
1
Jak więc zapobiec wyświetlaniu się strony, gdy renderuje wszystko? Poza tym, czy masz wbudowane style powłoki aplikacji, aby Twoja strona miała początkowy układ zamiast renderowania czegoś, z czego Lynx byłby dumny?
Chris Love
2
Wydaje się, że nadal dla mnie działa. Po wykonaniu tej czynności wiem już, że w PageSpeed ​​Insights pojawia się ostrzeżenie dotyczące tego pliku.
AndyWarren
3
to działa dla mnie (08 lipca -2018). I to daje dobry wynik pod względem szybkości stron
abilash er
1
IMHO, nowsza odpowiedź jabachetta używająca „preload” , jest obecnie prawdopodobnie preferowanym rozwiązaniem. Jeśli ktoś ma powód, by sądzić, że ta odpowiedź jest nadal preferowana, dodaj komentarz wyjaśniający dlaczego. Idealnie byłoby połączyć się z zasobem, który potwierdza, dlaczego / kiedy takie podejście może być preferowane. [Zakładając, że używasz polyfill do obsługi preloadFirefoksa i starszych przeglądarek - zobacz link w pierwszym komentarzu do tej odpowiedzi].
ToolmakerSteve
109

Aktualizacja 2020


Prosta odpowiedź (pełna obsługa przeglądarki):

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

Udokumentowana odpowiedź (z opcjonalnym ładowaniem wstępnym i funkcją awaryjną z wyłączoną obsługą skryptów):

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

Łączone ładowanie wstępne i asynchroniczne:

Jeśli potrzebujesz wstępnie załadowanego i asynchronicznego CSS, to rozwiązanie po prostu łączy dwie linie z udokumentowanej odpowiedzi powyżej, czyniąc ją nieco bardziej przejrzystą. Ale to nie zadziała w Firefoksie, dopóki nie obsługują słowa kluczowego preload . I znowu, jak szczegółowo opisano w udokumentowanej odpowiedzi powyżej, wstępne ładowanie może w rzeczywistości nie być korzystne.

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

Dodatkowe uwagi:

Zauważ, że generalnie, jeśli zamierzasz ładować CSS asynchronicznie, ogólnie zaleca się wbudowanie krytycznego CSS , ponieważ CSS jest zasobem blokującym renderowanie z jakiegoś powodu .

Podziękowania dla grupy filamentów za wiele asynchronicznych rozwiązań CSS.

jabacchetta
źródło
3
Nie ma potrzeby używania loadCSS, wystarczy polyfill
nathan
1
Stan specyfikacji wstępnego ładowania to ostatecznie [Rekomendacja kandydata W3C]. ( W3.org/TR/preload/ ... ) Firefox to obsługuje, ale nadal jest domyślnie wyłączony; kliknij łącze „powszechnie obsługiwane” w odpowiedzi, aby wyświetlić opis flagi przeglądarki Firefox, która ją kontroluje, „Czy mogę użyć”.
ToolmakerSteve
1
Zaktualizowano odpowiedź, aby zapewnić rozwiązanie umożliwiające pełną obsługę przeglądarki, wraz z dodatkowym wyjaśnieniem.
jabacchetta
1
@jabacchetta, aktualizacja 2020 jest bardzo ceniona, dziękuję. Szukałem konkretnie instrukcji napisanych ostatnio, w których obsługa przeglądarki jest ogólnie lepsza. Wiele się zmienia w ciągu 3 lat w sieci!
Brandito
Najwyraźniej lepiej onload="this.rel='stylesheet'; this.onload = null". Najwyraźniej konieczne jest ustawienie opcji, this.onloadaby nulluniknąć podwójnego wywoływania tego w niektórych przeglądarkach.
Flimm
9

Korzystanie media="print"ionload

Grupa filamentów niedawno (lipiec 2019 r.) Opublikowała artykuł zawierający najnowsze zalecenia dotyczące asynchronicznego ładowania CSS. Mimo że są twórcami popularnej biblioteki JavaScript loadCSS , w rzeczywistości polecają to rozwiązanie, które nie wymaga biblioteki Javascript:

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

Użycie media="print"wskaże przeglądarce, aby nie używać tego arkusza stylów na ekranach, ale na wydruku. Przeglądarki faktycznie pobierają te arkusze stylów drukowania, ale asynchronicznie, a tego właśnie chcemy. Chcemy również, aby arkusz stylów był używany po pobraniu i do tego ustawiliśmy onload="this.media='all'; this.onload = null". (Niektóre przeglądarki zadzwonią onloaddwa razy, aby obejść ten problem, musimy ustawić this.onload = null). Jeśli chcesz, możesz dodać <noscript>rezerwę dla rzadkich użytkowników, którzy nie mają włączonego Javascript.

Oryginalny artykuł warto przeczytać, jak to jest w sposób bardziej szczegółowy niż jestem tutaj. Warto przeczytać również ten artykuł na csswizardry.com .

Flimm
źródło
5

możesz spróbować go zdobyć na wiele sposobów:

1. używanie media="bogus"i <link>na stopie

<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.Wstawianie DOM w stary sposób

<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. jeśli możesz wypróbować wtyczki, możesz spróbować loadCSS

<script>
  // include loadCSS here...
  function loadCSS( href, before, media ){ ... }
  // load a file
  loadCSS( "path/to/mystylesheet.css" );
</script>
kamus
źródło
3
Przykład 2 ładuje Javascript, ale pytanie dotyczy ładowania CSS. Czy wiesz, czy przykład 2 działa również dla CSS, jeśli zmienisz z <script>na <style rel=stylesheet>? (Po prostu ciekawy. Zamierzam użyć loadCSS(tj. Twojego przykładu 3) zamiast tego, jeśli będę musiał załadować CSS później.)
KajMagnus
5

Poniższa funkcja utworzy i doda do dokumentu wszystkie arkusze stylów, które chcesz ładować asynchronicznie. (Ale dzięki Event Listener, zrobi to dopiero po załadowaniu wszystkich innych zasobów okna).

Zobacz następujące:

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
źródło
1
Czy są jakieś wady tej metody, jeśli porównamy ją powiedzmy z podejściem loadCSS? Wygląda na to, że klasyczny kod var newStyle = document.createElement("link"); newStyle.rel = "stylesheet"; newStyle.href = "stylesheet.css"; document.getElementsByTagName("head")[0].appendChild(newStyle);wewnątrz <script>tagu w treści strony działa doskonale - nawet w starszych przeglądarkach, takich jak MSIE8.
TecMan
2
@TecMan tak, są. Jak widać, ta funkcja wykonuje swoją pracę na window.loadzdarzenie. Tak więc pobieranie rozpoczyna się, gdy wszystko zostanie pobrane. Nie ma szczęścia. Aby rozpocząć, potrzebujesz szybkiego ładowania bez blokowania.
Miloš Đakonović
5

Asynchroniczne metody ładowania CSS

istnieje kilka sposobów, aby przeglądarka ładowała CSS asynchronicznie, ale żaden z nich nie jest tak prosty, jak można by się spodziewać.

<link rel="preload" href="mystyles.css" as="style" onload="this.rel='stylesheet'">
virender nehra
źródło
1

Jeśli potrzebujesz programowo i asynchronicznie załadować link CSS:

// 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
źródło
0

Jeśli masz ścisłą politykę bezpieczeństwa treści, które nie pozwala @ Vladimir-Salguero „s odpowiedź , można użyć tego (zanotuj skryptu 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>

Wystarczy dodać następujące odniesienia arkusza stylów: media="none" data-async="true". Oto przykład:

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

Przykład dla 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
źródło
Myślę, że asyncatrybut jest ignorowany, ponieważ tag skryptu nie musi srcładować się asynchronicznie ... czy jest to naprawdę przydatne tutaj? Czy możesz również wyjaśnić nieco więcej, której wartości użyć jako nonce?
Philipp,
0

Zaktualizuj odpowiedź, ponieważ wszystkie powyższe nie są teraz imponujące w statystykach Google Pagespeed.

Według Google w ten sposób należy zaimplementować ładowanie asynchroniczne 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
źródło
1
Prawdopodobnie fałszywie dodatni (błąd PageSpeed), jeśli używasz metody wstępnego ładowania słów kluczowych, o której wspomniałem. github.com/filamentgroup/loadCSS/issues/53
jabacchetta
1
AKTUALIZACJA: Firma Google wycofała, a następnie zamknęła wersję 4 programu PageSpeed, w której wystąpił ten problem - była to wersja, dla której zalecano ten kod asynchroniczny. Chociaż nie testowałem w PageSpeed ​​5 : biorąc pod uwagę opis tego, jak teraz testują i ich obsługę dla tagu „preload” w „link”, odpowiedź jabachetta jest teraz prawdopodobnie lepszą odpowiedzią dla Google.
ToolmakerSteve
1
@ToolmakerSteve Tak Ta odpowiedź musi być często moderowana. Ale ten kod nadal działa, aby uzyskać 100 w Page Speed.
kaushik gandhi
0

Spróbowałem użyć:

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

Działa dobrze, ale podnosi również skumulowane przesunięcie układu, ponieważ kiedy używamy rel = "preload", po prostu pobiera css, a nie stosuje natychmiast.

Na przykład, gdy DOM ładuje listę zawierającą znaczniki ul, li, domyślnie przed znacznikami li znajduje się punktor, a następnie zastosowano CSS, aby usunąć te punktory z niestandardowych stylów do wyświetlenia. A więc skumulowana zmiana układu ma miejsce tutaj.

Czy jest na to jakieś rozwiązanie?

n2lose
źródło
0

Użyj, rel="preload"aby pobrać go niezależnie, a następnie użyj, onload="this.rel='stylesheet'"aby zastosować go do arkusza stylów ( as="style"konieczne jest zastosowanie go do arkusza stylów, w przeciwnym razie onloadnie zadziała)

<link rel="preload" as="style" type="text/css" href="mystyles.css" onload="this.rel='stylesheet'">
Shammi Hans
źródło