Cara memuat CSS secara asinkron

93

Saya mencoba menghilangkan 2 file CSS yang membuat pemblokiran di situs saya - file tersebut muncul di Google Page Speed ​​Insights. Saya telah mengikuti metode yang berbeda, tidak satupun yang berhasil. Tapi, baru-baru ini, saya menemukan sebuah posting tentang Thinking Async dan ketika saya menerapkan kode ini: <script async src="https://third-party.com/resource.js"></script>itu menghilangkan masalah.

Namun, setelah dipublikasikan, halaman kehilangan gaya. Saya tidak terlalu yakin dengan apa yang terjadi karena kodenya berfungsi tetapi gaya setelah unggahan yang tidak berfungsi. Akan sangat menghargai bantuan Anda dalam hal ini. Terima kasih

Paulina994
sumber
2
Apakah Anda menerapkan asinkron pada gaya atau skrip? Gaya memuat beberapa saat setelah Anda memuat halaman atau tidak pernah muncul?
kamus
Saya menerapkan atribut async ke gaya dan menempatkannya di header.
Paulina994
2
Masalah dengan gaya adalah bahwa rendering ulang akan dipicu jika Anda memuatnya terlambat (misalnya di dalam body), dan tidak diizinkan dalam standar (tetapi karena browser sangat pemaaf, itu akan tetap berfungsi). Jika masalahnya adalah waktu respons yang lambat dari server pihak ketiga, mungkin Anda dapat menghostingnya di server Anda saja?
Josef Engelfrost

Jawaban:

129

Trik untuk memicu unduhan stylesheet asinkron adalah dengan menggunakan <link>elemen dan menyetel nilai yang tidak valid untuk atribut media (saya menggunakan media = "none", tetapi nilai apa pun bisa digunakan). Saat kueri media bernilai false, browser akan tetap mendownload stylesheet, tetapi tidak akan menunggu konten tersedia sebelum merender halaman.

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

Setelah stylesheet selesai mengunduh atribut media harus disetel ke nilai yang valid sehingga aturan gaya akan diterapkan ke dokumen. Peristiwa onload digunakan untuk mengalihkan properti media ke semua:

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

Metode pemuatan CSS ini akan memberikan konten yang dapat digunakan kepada pengunjung lebih cepat daripada pendekatan standar. CSS kritis masih dapat disajikan dengan pendekatan pemblokiran biasa (atau Anda dapat menyebariskannya untuk performa terbaik) dan gaya non-kritis dapat diunduh dan diterapkan secara progresif nanti dalam proses parsing / rendering.

Teknik ini menggunakan JavaScript, tetapi Anda dapat melayani browser non-JavaScript dengan membungkus <link>elemen pemblokiran yang setara dalam sebuah <noscript>elemen:

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

Anda dapat melihat operasinya di www.itcha.edu.sv

masukkan deskripsi gambar di sini

Sumber di http://keithclark.co.uk/

Vladimir Salguero
sumber
Ok .. Saya tidak tahu bahwa Anda bisa mereferensikan atribut elemen di penangan hanya dengan menyebutkan namanya. Saya selalu berpikir 'acara' adalah satu-satunya pengecualian.
Teemoh
1
Jadi, bagaimana Anda mencegah halaman Anda berkedip saat merender semuanya? Selain itu, apakah Anda menyebariskan gaya shell aplikasi inti sehingga halaman Anda memiliki beberapa tata letak awal alih-alih menampilkan sesuatu yang bisa dibanggakan Lynx?
Chris Love
2
Sepertinya masih bekerja untuk saya. Setelah melakukan ini saya tahu lagi mendapatkan peringatan di PageSpeed ​​Insights untuk file ini.
AndyWarren
3
ini berfungsi untuk saya (08-Juli -2018). Dan itu memberikan skor yang bagus dalam wawasan
kecepatan halaman
1
IMHO, jawaban terbaru jabachetta menggunakan "preload" , kemungkinan adalah solusi yang lebih disukai sekarang. Jika seseorang memiliki alasan untuk berpikir ini jawabannya masih lebih baik, silahkan menambahkan komentar menjelaskan mengapa. Idealnya, menghubungkan ke sumber daya yang mengonfirmasi mengapa / kapan pendekatan ini masih mungkin lebih disukai. [Dengan asumsi Anda menggunakan polyfill untuk mendukung preloaddi Firefox dan di browser lama - lihat tautan di komentar pertama jawaban itu].
ToolmakerSteve
110

Pembaruan 2020


Jawaban sederhana (dukungan browser lengkap):

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

Jawaban yang didokumentasikan (dengan pramuat opsional dan fallback skrip nonaktif):

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

Pra-pemuatan dan gabungan asinkron:

Jika Anda membutuhkan CSS pramuat dan asinkron, solusi ini hanya menggabungkan dua baris dari jawaban yang didokumentasikan di atas, membuatnya sedikit lebih bersih. Tetapi ini tidak akan berfungsi di Firefox sampai mereka mendukung kata kunci pramuat . Dan sekali lagi, seperti yang dijelaskan dalam jawaban yang didokumentasikan di atas, pramuat sebenarnya tidak bermanfaat.

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

Pertimbangan tambahan:

Perhatikan bahwa, secara umum, jika Anda akan memuat CSS secara asinkron, umumnya disarankan agar Anda memasukkan CSS kritis sebaris , karena CSS adalah sumber daya yang memblokir perenderan karena suatu alasan .

Penghargaan untuk grup filamen untuk banyak solusi CSS asinkron mereka.

jabacchetta.dll
sumber
3
Tidak perlu menggunakan loadCSS, polyfill sudah cukup: github.com/filamentgroup/loadCSS/blob/master/src/…
nathan
1
Status spesifikasi pramuat akhirnya adalah [Rekomendasi Kandidat W3C]. ( W3.org/TR/preload/… ) Firefox mendukungnya, tetapi masih dinonaktifkan secara default; klik tautan jawaban yang "didukung secara luas" untuk deskripsi "Dapatkah Saya Menggunakan" tentang bendera Firefox yang mengontrolnya.
ToolmakerSteve
1
Jawaban diperbarui untuk memberikan solusi untuk dukungan browser lengkap, bersama dengan penjelasan tambahan.
jabacchetta
1
@jabacchetta update tahun 2020 sangat diapresiasi terima kasih. Saya secara khusus mencari petunjuk yang ditulis belakangan ini di mana dukungan browser umumnya lebih baik. Banyak perubahan selama 3 tahun di web!
Brandito
Rupanya, lebih baik onload="this.rel='stylesheet'; this.onload = null". Ini perlu diatur this.onloaduntuk nullmenghindari ini dipanggil dua kali di beberapa browser, tampaknya.
Flimm
9

Menggunakan media="print"danonload

Grup filamen baru-baru ini (Juli 2019) menerbitkan sebuah artikel yang memberikan rekomendasi terbaru mereka tentang cara memuat CSS secara asinkron. Meskipun mereka adalah pengembang pustaka Javascript loadCSS yang populer , mereka sebenarnya merekomendasikan solusi ini yang tidak memerlukan pustaka Javascript:

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

Menggunakan media="print"akan menunjukkan kepada browser untuk tidak menggunakan stylesheet ini pada layar, tetapi pada cetakan. Peramban sebenarnya mengunduh lembar gaya cetak ini, tetapi secara asinkron, itulah yang kami inginkan. Kami juga ingin stylesheet digunakan setelah diunduh, dan untuk itu kami tetapkan onload="this.media='all'; this.onload = null". (Beberapa browser akan menelepon onloaddua kali, untuk mengatasinya, kami perlu menyetelnya this.onload = null.) Jika mau, Anda dapat menambahkan <noscript>fallback untuk pengguna langka yang tidak mengaktifkan Javascript.

The artikel asli bernilai membaca, karena masuk ke detail lebih dari saya di sini. Artikel di csswizardry.com ini juga patut untuk dibaca.

Flimm
sumber
5

Anda dapat mencoba mendapatkannya dengan banyak cara:

1. menggunakan media="bogus"dan <link>di kaki

<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. Memasukkan DOM dengan cara lama

<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. Jika Anda dapat mencoba plugin, Anda dapat mencoba loadCSS

<script>
  // include loadCSS here...
  function loadCSS( href, before, media ){ ... }
  // load a file
  loadCSS( "path/to/mystylesheet.css" );
</script>
kamus
sumber
3
Contoh 2 memuat Javascript, tetapi pertanyaannya adalah tentang memuat CSS. Tahukah Anda jika contoh 2 juga berfungsi untuk CSS, jika Anda mengubah dari <script>menjadi <style rel=stylesheet>? (Hanya ingin tahu. Saya akan menggunakan loadCSS(contoh 3 Anda) sebagai gantinya, jika saya perlu memuat CSS nanti.)
KajMagnus
5

Fungsi di bawah ini akan membuat dan menambahkan ke dokumen semua stylesheet yang ingin Anda muat secara asinkron. (Namun, berkat Event Listener, itu hanya akan melakukannya setelah semua sumber daya jendela lainnya dimuat.)

Lihat berikut ini:

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
sumber
1
Apakah ada kekurangan dari metode ini jika kita membandingkannya dengan pendekatan loadCSS? Tampaknya, kode klasik var newStyle = document.createElement("link"); newStyle.rel = "stylesheet"; newStyle.href = "stylesheet.css"; document.getElementsByTagName("head")[0].appendChild(newStyle);di dalam <script>tag di badan halaman bekerja dengan sangat baik - bahkan di browser lama seperti MSIE8.
TecMan
2
@TecMan ya ada. Seperti yang Anda lihat, fungsi ini berfungsi saat window.loadacara. Jadi, unduhan dimulai saat semuanya diunduh. Tidak beruntung di sana. Anda memerlukan pemuatan tanpa pemblokiran secepat mungkin untuk memulai.
Miloš Đakonović
5

Pendekatan Pemuatan Asinkron CSS

Ada beberapa cara untuk membuat browser memuat CSS secara asinkron, meskipun tidak ada yang sesederhana yang Anda duga.

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

Jika Anda perlu memuat tautan CSS secara terprogram dan asinkron:

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

Jika Anda memiliki kebijakan keamanan konten yang ketat yang tidak memungkinkan @ vladimir-Salguero 's jawaban , Anda dapat menggunakan ini (silakan membuat catatan dari 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>

Cukup tambahkan berikut ini untuk referensi stylesheet Anda: media="none" data-async="true". Berikut contohnya:

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

Contoh untuk 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
sumber
Saya pikir asyncatribut ini diabaikan, karena tag skrip tidak memiliki srcuntuk memuat secara asinkron ... atau benar-benar berguna di sini? Juga dapatkah Anda menguraikan lebih banyak tentang nilai mana yang akan digunakan sebagai nonce?
Philipp
0

Harap berhati-hati untuk memperbarui jawaban karena semua hal di atas gagal mengesankan wawasan google pagespeed sekarang.

Menurut Google, ini adalah bagaimana Anda harus mengimplementasikan pemuatan asinkron dari 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
sumber
1
Kemungkinan positif palsu (bug PageSpeed) jika Anda menggunakan pendekatan kata kunci preload yang saya sebutkan. github.com/filamentgroup/loadCSS/issues/53
jabacchetta
1
PEMBARUAN: Google tidak lagi digunakan, lalu mematikan, PageSpeed ​​versi 4 yang memiliki masalah ini - versi yang disarankan untuk kode asinkron ini. Meskipun saya belum menguji di PageSpeed ​​5 : mengingat deskripsi cara mereka menguji sekarang, dan dukungan mereka untuk tag "preload" dari "link", jawaban jabachetta kemungkinan merupakan jawaban yang lebih baik sekarang, untuk Google.
ToolmakerSteve
1
@ToolmakerSteve Ya Jawaban ini perlu sering dimoderasi. Tetapi kode ini masih berfungsi untuk memberi Anda 100 dalam Kecepatan Halaman.
kaushik gandhi
0

Saya telah mencoba menggunakan:

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

Ini berfungsi dengan baik, tetapi juga meningkatkan pergeseran tata letak kumulatif karena ketika kita menggunakan rel = "preload", itu hanya mengunduh css, tidak langsung diterapkan.

Contoh ketika DOM memuat daftar berisi ul, tag li, ada peluru sebelum tag li secara default, lalu CSS diterapkan sehingga saya menghapus peluru ini ke gaya khusus untuk daftar. Sehingga, terjadi pergeseran tata letak kumulatif di sini.

Apakah ada solusi untuk itu?

n2lose
sumber
0

Gunakan rel="preload"untuk membuatnya mendownload secara mandiri, lalu gunakan onload="this.rel='stylesheet'"untuk menerapkannya ke stylesheet ( as="style"perlu untuk menerapkannya ke stylesheet jika onloadtidak, tidak akan berfungsi)

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