Як завантажити CSS асинхронно


93

Я намагаюся усунути 2 файли CSS, які блокують зображення на моєму сайті - вони з’являються в Google Page Speed ​​Insights. Я дотримувався різних методів, жоден з яких не мав успіху. Але нещодавно я знайшов допис про Thinking Async і коли я застосував цей код: <script async src="https://third-party.com/resource.js"></script>він усунув проблему.

Однак після публікації сторінка втратила стиль. Я не надто впевнений у тому, що відбувається, тому що код працює, але це стиль після завантаження не працює. Будемо вдячні за вашу допомогу в цьому. Дякую


2
Чи застосовуєте ви асинхронізацію до стилів чи сценаріїв? Стиль завантажується через деякий час після завантаження сторінки або вона ніколи не з’являється?
kamus

Я застосував атрибут async до стилів і розмістив їх у заголовку.
Paulina994

2
Річ у стилях полягає в тому, що повторний візуалізація спрацює, якщо ви завантажите їх із запізненням (наприклад, у тілі), і це не дозволено в стандартах (але оскільки браузери дуже прощають, це все одно спрацює). Якщо проблема полягає в повільному часі відгуку від стороннього сервера, можливо, ви можете просто розмістити їх на своєму сервері?
Йозеф Енгельфрост,

Відповіді:


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/


Гаразд .. Я не знав, що ви можете посилатись на атрибути елементів у обробниках, просто згадуючи їх ім’я. Я завжди думав, що "подія" - це єдиний виняток.
Тімох

1
Отже, як уберегти вашу сторінку від мигання, оскільки вона відображає все? Крім того, ви вбудовуєте основні стилі оболонки програми, щоб ваша сторінка мала якийсь початковий макет, замість того, щоб зобразити щось, чим Lynx пишався б?
Chris Love

2
Здається, це все ще працює для мене. Після цього я знаю, що більше отримую попередження в PageSpeed ​​Insights щодо цього файлу.
AndyWarren,

3
це працює для мене (08 липня-2018). І це дає хороший бал у проникненні
сторінок

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>

Попереднє завантаження та асинхронна комбінація:

Якщо вам потрібен попередньо завантажений та асинхронний 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 є причиною, що блокує рендер .

Кредит групі ниток за їх безліч асинхронних рішень CSS.


3
Немає необхідності використовувати loadCSS, достатньо поліфілу
Натан

1
Статус попереднього завантаження попередньо встановлений [Рекомендація кандидата W3C]. ( W3.org/TR/preload/… ) Firefox підтримує це, але за замовчуванням все ще вимкнено; клацніть на посилання "широко підтримується" відповіді для опису прапора Firefox, який керує ним, "Чи можу я використовувати".
ToolmakerSteve

1
Відповідь оновлено, щоб надати рішення для повної підтримки браузера, а також додаткові пояснення.
jabacchetta

1
@jabacchetta оновлення 2020 дуже вдячне, дякую. Я шукав спеціально інструкції, написані останнім часом, де підтримка браузера, як правило, краща. Багато змін за 3 роки в Інтернеті!
Брендіто

Мабуть, так краще onload="this.rel='stylesheet'; this.onload = null". Потрібно встановити this.onloadна, nullщоб уникнути цього, щоб у деяких браузерах це не викликалося двічі, мабуть.
Флімм

9

Використання media="print"таonload

Нещодавно (в липні 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"та a <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

Підходи до завантаження асинхронного 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

Якщо у вас є сувора політика захисту контенту , яка не дозволяє @ володимир-Салгуеро «s відповідь , ви можете використовувати це (будь ласка , зверніть увагу на сценарій 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?
Філіпп

0

Будь ласка, оновіть відповідь, оскільки все вищезазначене не справляє враження на сторінку швидкості Google.

На думку Google, саме так слід реалізувати асинхронне завантаження 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
Ймовірно, помилково позитивний результат (помилка PageSpeed), якщо ви використовуєте підхід до попереднього завантаження ключового слова, про який я згадав. github.com/filamentgroup/loadCSS/issues/53
jabacchetta

1
ОНОВЛЕННЯ: Google припинив, а потім вимкнув PageSpeed ​​версії 4, яка мала цю проблему - саме та версія була рекомендована для цього асинхронного коду. Хоча я не тестував у PageSpeed ​​5 : враховуючи опис того, як вони тестують зараз, та їх підтримку тегу "попереднього завантаження" "посилання", відповідь Джабачетти, швидше за все, зараз є кращою відповіддю для Google.
ToolmakerSteve

1
@ToolmakerSteve Так Цю відповідь потрібно часто модерувати. Але цей код все ще працює, щоб отримати 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'">
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.