วิธีโหลด CSS Asynchronously


93

ฉันกำลังพยายามกำจัดไฟล์ CSS 2 ไฟล์ที่แสดงการบล็อกบนไซต์ของฉันซึ่งจะปรากฏใน Google Page Speed ​​Insights ฉันได้ทำตามวิธีการต่างๆ แต่ไม่มีวิธีใดที่ประสบความสำเร็จ แต่เมื่อเร็ว ๆ นี้ฉันพบโพสต์เกี่ยวกับThinking Asyncและเมื่อฉันใช้รหัสนี้<script async src="https://third-party.com/resource.js"></script>มันช่วยขจัดปัญหาได้

อย่างไรก็ตามหลังจากเผยแพร่เพจก็สูญเสียรูปแบบไป ฉันไม่แน่ใจเหมือนกันว่าเกิดอะไรขึ้นเพราะโค้ดใช้งานได้ แต่เป็นการจัดแต่งทรงผมหลังจากอัปโหลดที่ใช้ไม่ได้ ขอขอบคุณสำหรับความช่วยเหลือของคุณในเรื่องนี้ ขอบคุณ


2
คุณใช้ async กับสไตล์หรือสคริปต์หรือไม่? สไตล์จะโหลดสักพักหลังจากที่คุณโหลดหน้าหรือไม่ปรากฏ
kamus

ฉันใช้แอตทริบิวต์ async กับสไตล์และวางไว้ในส่วนหัว
Paulina994

2
สิ่งที่มีสไตล์คือการเรนเดอร์จะถูกทริกเกอร์หากคุณโหลดช้า (เช่นในเนื้อหา) และไม่ได้รับอนุญาตในมาตรฐาน (แต่เนื่องจากเบราว์เซอร์มีการให้อภัยอย่างมากจึงจะใช้งานได้) หากปัญหาคือเวลาตอบสนองช้าจากเซิร์ฟเวอร์ของบุคคลที่สามบางทีคุณสามารถโฮสต์พวกเขาบนเซิร์ฟเวอร์ของคุณแทน?
Josef Engelfrost

คำตอบ:


129

เคล็ดลับในการเรียกการดาวน์โหลดสไตล์ชีตแบบอะซิงโครนัสคือการใช้<link>องค์ประกอบและตั้งค่าที่ไม่ถูกต้องสำหรับแอตทริบิวต์สื่อ (ฉันใช้ media = "none" แต่จะมีค่าใด ๆ ) เมื่อแบบสอบถามสื่อประเมินว่าเป็นเท็จเบราว์เซอร์จะยังคงดาวน์โหลดสไตล์ชีต แต่จะไม่รอให้เนื้อหาพร้อมใช้งานก่อนที่จะแสดงผลหน้า

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

เมื่อดาวน์โหลดสไตล์ชีตเสร็จแล้วจะต้องตั้งค่าแอตทริบิวต์สื่อเป็นค่าที่ถูกต้องจึงจะนำกฎสไตล์ไปใช้กับเอกสาร เหตุการณ์ onload ใช้เพื่อเปลี่ยนคุณสมบัติสื่อเป็นทั้งหมด:

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

วิธีการโหลด CSS นี้จะนำเสนอเนื้อหาที่เป็นประโยชน์แก่ผู้เยี่ยมชมได้เร็วกว่าวิธีมาตรฐาน CSS ที่สำคัญยังคงสามารถใช้งานได้ด้วยวิธีการบล็อกตามปกติ (หรือคุณสามารถอินไลน์เพื่อประสิทธิภาพสูงสุด) และสไตล์ที่ไม่สำคัญสามารถดาวน์โหลดและนำไปใช้ในขั้นตอนการแยกวิเคราะห์ / การแสดงผลในภายหลังได้

เทคนิคนี้ใช้ JavaScript แต่คุณสามารถรองรับเบราว์เซอร์ที่ไม่ใช่ JavaScript ได้โดยการรวม<link>องค์ประกอบการบล็อกที่เทียบเท่าไว้ใน<noscript>องค์ประกอบ:

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

คุณสามารถดูการดำเนินการได้ในwww.itcha.edu.sv

ป้อนคำอธิบายภาพที่นี่

ที่มาในhttp://keithclark.co.uk/


โอเค .. ฉันไม่รู้ว่าคุณสามารถอ้างอิงแอตทริบิวต์ขององค์ประกอบใน on-handlers ได้เพียงแค่เอ่ยชื่อ ฉันคิดเสมอว่า 'เหตุการณ์' เป็นข้อยกเว้นเพียงอย่างเดียว
Teemoh

1
แล้วคุณจะป้องกันไม่ให้เพจของคุณกระพริบได้อย่างไรเมื่อมันแสดงผลทุกอย่าง? นอกจากนี้คุณยังใช้สไตล์เชลล์ของแอปหลักแบบอินไลน์เพื่อให้หน้าของคุณมีเค้าโครงเริ่มต้นแทนที่จะแสดงสิ่งที่ Lynx จะภาคภูมิใจ
Chris Love

2
ดูเหมือนจะยังใช้ได้สำหรับฉัน หลังจากทำเช่นนี้ฉันรู้ว่าได้รับคำเตือนใน PageSpeed ​​Insights สำหรับไฟล์นี้นานขึ้น
AndyWarren

3
สิ่งนี้ใช้ได้ผลสำหรับฉัน (08- กรกฎาคม -2018) และให้คะแนนที่ดีในข้อมูลเชิงลึกของ
Pagespeed

1
IMHO ซึ่งเป็นคำตอบที่ใหม่กว่าของ jabachetta โดยใช้ "พรีโหลด"น่าจะเป็นทางออกที่ดีกว่าในตอนนี้ ถ้าใครมีเหตุผลที่จะคิดว่านี้คำตอบคือยังคงดีกว่าโปรดเพิ่มความคิดเห็นอธิบายว่าทำไม การเชื่อมโยงไปยังแหล่งข้อมูลที่ยืนยันว่าเหตุใด / เมื่อวิธีนี้ยังคงเป็นวิธีที่ดีกว่า [สมมติว่าคุณใช้ polyfill เพื่อรองรับpreloadFirefox และเบราว์เซอร์รุ่นเก่า - ดูลิงก์ในความคิดเห็นแรกของคำตอบนั้น]
ToolmakerSteve

110

อัปเดตปี 2020


คำตอบง่ายๆ (รองรับเบราว์เซอร์เต็มรูปแบบ):

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

คำตอบที่เป็นเอกสาร (พร้อมทางเลือกในการโหลดล่วงหน้าและทางเลือกที่ปิดใช้งานสคริปต์):

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

การโหลดล่วงหน้าและ async รวมกัน:

หากคุณต้องการ CSS ที่โหลดไว้ล่วงหน้าและไม่ซิงค์โซลูชันนี้จะรวมสองบรรทัดจากคำตอบที่ระบุไว้ด้านบนทำให้สะอาดขึ้นเล็กน้อย แต่นี้จะไม่ทำงานใน Firefoxจนกว่าพวกเขาจะสนับสนุนคำหลักพรีโหลด และอีกครั้งตามรายละเอียดในคำตอบด้านบนการโหลดล่วงหน้าอาจไม่เป็นประโยชน์

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

ข้อควรพิจารณาเพิ่มเติม:

โปรดทราบว่าโดยทั่วไปถ้าคุณกำลังจะโหลด CSS แบบไม่พร้อมก็ขอแนะนำโดยทั่วไปว่าคุณอินไลน์CSS ที่สำคัญตั้งแต่ CSS เป็นทรัพยากรที่บล็อกการแสดงผลสำหรับเหตุผล

ให้เครดิตกลุ่ม filamentสำหรับโซลูชัน async CSS จำนวนมาก


3
ไม่จำเป็นต้องใช้ loadCSS โพลีฟิลก็เพียงพอแล้ว: github.com/filamentgroup/loadCSS/blob/master/src/…
ธาน

1
สถานะข้อมูลจำเพาะของ Preload คือในที่สุด [คำแนะนำสำหรับผู้สมัคร W3C] ( w3.org/TR/preload/… ) Firefox รองรับ แต่ยังคงปิดใช้งานโดยค่าเริ่มต้น คลิกลิงก์ "รองรับอย่างกว้างขวาง" ของคำตอบสำหรับคำอธิบาย "ใช้ของฉันได้" ของธง Firefox ที่ควบคุม
ToolmakerSteve

1
คำตอบได้รับการอัปเดตเพื่อมอบโซลูชันสำหรับการสนับสนุนเบราว์เซอร์เต็มรูปแบบพร้อมกับคำอธิบายเพิ่มเติม
jabacchetta

1
@jabacchetta การอัปเดตปี 2020 ได้รับการชื่นชมอย่างมากขอบคุณ ฉันกำลังมองหาคำแนะนำที่เขียนขึ้นโดยเฉพาะในช่วงเวลาที่ผ่านมาซึ่งโดยทั่วไปแล้วการรองรับเบราว์เซอร์จะดีกว่า การเปลี่ยนแปลงมากมายตลอด 3 ปีบนเว็บ!
Brandito

onload="this.rel='stylesheet'; this.onload = null"เห็นได้ชัดว่ามันจะดีกว่า จำเป็นต้องตั้งค่าthis.onloadเพื่อnullหลีกเลี่ยงไม่ให้มีการเรียกซ้ำสองครั้งในบางเบราว์เซอร์
Flimm

9

การใช้media="print"และonload

กลุ่ม Filament เมื่อเร็ว ๆ นี้ (กรกฎาคม 2019) ได้เผยแพร่บทความที่ให้คำแนะนำล่าสุดเกี่ยวกับวิธีโหลด CSS แบบอะซิงโครนัส แม้ว่าพวกเขาจะเป็นผู้พัฒนาไลบรารี Javascript ยอดนิยมก็ตาม loadCSSแต่จริงๆแล้วพวกเขาแนะนำโซลูชันนี้ที่ไม่ต้องการไลบรารี Javascript:

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

การใช้media="print"จะเป็นการระบุให้เบราว์เซอร์ไม่ใช้สไตล์ชีทนี้บนหน้าจอ แต่เป็นการพิมพ์ เบราว์เซอร์จะดาวน์โหลดสไตล์ชีตการพิมพ์เหล่านี้ แต่แบบอะซิงโครนัสซึ่งเป็นสิ่งที่เราต้องการ onload="this.media='all'; this.onload = null"นอกจากนี้เรายังต้องการสไตล์ที่จะใช้เมื่อมีการดาวน์โหลดและสำหรับชุดเราว่า (เบราว์เซอร์บางตัวจะเรียกonloadสองครั้งเพื่อหลีกเลี่ยงปัญหานั้นเราจำเป็นต้องตั้งค่าthis.onload = null) หากคุณต้องการคุณสามารถเพิ่มทาง<noscript>เลือกสำหรับผู้ใช้ที่หายากที่ไม่ได้เปิดใช้งาน Javascript

บทความเดิมมีมูลค่าการอ่านเป็นมันไปลงในรายละเอียดมากขึ้นกว่าที่ฉันอยู่ที่นี่ บทความนี้ใน csswizardry.comก็น่าอ่านเช่นกัน


5

คุณสามารถลองได้หลายวิธี:

1. การใช้media="bogus"และ<link>ที่เท้า

<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. การใส่ DOM ด้วยวิธีเดิม

<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. หากคุณสามารถลองใช้ปลั๊กอินได้คุณสามารถลอง loadCSS

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

3
ตัวอย่างที่ 2 โหลด Javascript แต่คำถามเกี่ยวกับการโหลด CSS คุณรู้หรือไม่ตัวอย่างเช่น 2 ทำงานยังสำหรับ CSS, ถ้าคุณเปลี่ยนจาก<script>ไป<style rel=stylesheet>? (แค่อยากรู้อยากเห็นฉันจะใช้loadCSS(เช่นตัวอย่างของคุณ 3) แทนถ้าฉันต้องการโหลด CSS ในภายหลัง)
KajMagnus

5

ฟังก์ชันด้านล่างนี้จะสร้างและเพิ่มสไตล์ชีททั้งหมดที่คุณต้องการโหลดแบบอะซิงโครนัสลงในเอกสาร (แต่ต้องขอบคุณEvent Listenerมันก็ต่อเมื่อทรัพยากรอื่น ๆ ของหน้าต่างโหลดหมดแล้ว)

ดูสิ่งต่อไปนี้:

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
มีข้อเสียของวิธีนี้หรือไม่ถ้าเราเปรียบเทียบกับวิธี loadCSS? ดูเหมือนว่าโค้ดคลาสสิกvar newStyle = document.createElement("link"); newStyle.rel = "stylesheet"; newStyle.href = "stylesheet.css"; document.getElementsByTagName("head")[0].appendChild(newStyle);ภายใน<script>แท็กในเนื้อหาของเพจจะทำงานได้ดีแม้ในเบราว์เซอร์รุ่นเก่าเช่น MSIE8
TecMan

2
@TecMan ใช่มี. อย่างที่คุณเห็นฟังก์ชั่นนี้ใช้ได้กับwindow.loadเหตุการณ์ ดังนั้นการดาวน์โหลดจะเริ่มขึ้นเมื่อทุกอย่างถูกดาวน์โหลด ไม่มีโชคที่นั่น คุณต้องไม่ปิดกั้นการโหลดโดยเร็วที่สุดเพื่อเริ่มต้น
MilošĐakonović

5

แนวทางการโหลด Async CSS

มีหลายวิธีในการทำให้เบราว์เซอร์โหลด CSS แบบอะซิงโครนัสแม้ว่าจะไม่มีวิธีใดที่ง่ายอย่างที่คุณคาดหวัง

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

1

หากคุณต้องการโหลดลิงก์ 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);
}

0

หากคุณมีนโยบายการรักษาความปลอดภัยเนื้อหาที่เข้มงวดซึ่งไม่อนุญาตให้ใช้คำตอบของ@vladimir-salgueroคุณสามารถใช้สิ่งนี้ (โปรดสังเกตสคริปต์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>

media="none" data-async="true"เพียงแค่เพิ่มต่อไปนี้เพื่อการอ้างอิงสไตล์ชีตของคุณ: นี่คือตัวอย่าง:

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

ตัวอย่างสำหรับ 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>

ฉันคิดว่าasyncแอตทริบิวต์ถูกละเว้นเนื่องจากแท็กสคริปต์ไม่มีการsrcโหลดแบบอะซิงโครนัส ... หรือมีประโยชน์จริงๆที่นี่? คุณสามารถอธิบายเพิ่มเติมได้อีกเล็กน้อยเกี่ยวกับค่าที่จะใช้เป็นnonce?
Philipp

0

โปรดอัปเดตคำตอบเนื่องจากทั้งหมดข้างต้นไม่สามารถสร้างความประทับใจให้กับข้อมูลเชิงลึกของ Google Pagespeed ได้ในขณะนี้

ตามGoogleนี่คือวิธีที่คุณควรใช้การโหลด Css แบบ async

 < 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
อาจเป็นผลบวกที่ผิดพลาด (ข้อผิดพลาดของ PageSpeed) หากคุณใช้วิธีการโหลดคีย์เวิร์ดล่วงหน้าที่ฉันกล่าวถึง github.com/filamentgroup/loadCSS/issues/53
jabacchetta

1
อัปเดต: Google ได้เลิกใช้งานแล้วและปิดตัวลง PageSpeed ​​เวอร์ชัน 4 ที่มีปัญหานี้ซึ่งเป็นเวอร์ชันที่แนะนำให้ใช้รหัส async นี้ แม้ว่าฉันจะไม่ได้ทดสอบใน PageSpeed ​​5 : ให้คำอธิบายว่าพวกเขาทดสอบตอนนี้อย่างไรและการสนับสนุนแท็ก "โหลดล่วงหน้า" ของ "ลิงก์" แต่คำตอบของ jabachettaน่าจะเป็นคำตอบที่ดีกว่าสำหรับ Google ในตอนนี้
ToolmakerSteve

1
@ToolmakerSteve ใช่คำตอบนี้ต้องมีการกลั่นกรองบ่อยๆ แต่รหัสนี้ยังคงใช้งานได้เพื่อให้คุณได้รับ Page Speed ​​100
kaushik gandhi

0

ฉันได้ลองใช้:

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

มันใช้งานได้ค่าปรับ แต่มันยังเพิ่มเลย์เอาต์สะสมเพราะเมื่อเราใช้ rel = "preload" มันก็แค่ดาวน์โหลด css ไม่ได้ใช้ทันที

ตัวอย่างเมื่อ DOM โหลดรายการมีแท็ก ul, li มีสัญลักษณ์แสดงหัวข้อย่อยก่อนแท็ก li ตามค่าเริ่มต้นจากนั้น CSS จะใช้ว่าฉันลบสัญลักษณ์แสดงหัวข้อย่อยเหล่านี้เป็นสไตล์ที่กำหนดเองสำหรับการแสดงรายการ ดังนั้นการเลื่อนเค้าโครงสะสมจึงเกิดขึ้นที่นี่

มีวิธีแก้ไขหรือไม่?


0

ใช้rel="preload"เพื่อให้ดาวน์โหลดโดยอิสระจากนั้นใช้onload="this.rel='stylesheet'"เพื่อนำไปใช้กับสไตล์ชีท ( as="style"จำเป็นต้องใช้กับสไตล์ชีทมิฉะนั้นonloadจะใช้ไม่ได้)

<link rel="preload" as="style" type="text/css" href="mystyles.css" onload="this.rel='stylesheet'">
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.