Как загрузить CSS асинхронно

92

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

Однако после публикации страница потеряла стиль. Я не совсем уверен в том, что происходит, потому что код работает, но стиль после загрузки не работает. Был бы признателен за вашу помощь в этом. Благодарность

Паулина994
источник
2
Вы применяете асинхронность к стилям или скриптам? Стиль загружается через некоторое время после загрузки страницы или никогда не появляется?
Камус
Я применил атрибут async к стилям и поместил их в заголовок.
Paulina994,
2
Проблема со стилями в том, что повторный рендеринг будет запущен, если вы загрузите их поздно (например, в теле), и это не разрешено стандартами (но, поскольку браузеры очень снисходительны, он все равно будет работать). Если проблема заключается в медленном времени отклика стороннего сервера, возможно, вы можете просто разместить их на своем сервере?
Йозеф Энгельфрост,

Ответы:

129

Уловка для запуска асинхронной загрузки таблицы стилей заключается в использовании <link>элемента и установке недопустимого значения для атрибута media (я использую media = "none", но подойдет любое значение). Когда медиа-запрос оценивается как ложный, браузер все равно загрузит таблицу стилей, но не будет ждать, пока контент станет доступным, прежде чем отобразить страницу.

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

После завершения загрузки таблицы стилей атрибут мультимедиа должен иметь допустимое значение, чтобы правила стиля были применены к документу. Событие onload используется для переключения свойства media на all:

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

Владимир Сальгеро
источник
Хорошо .. Я не знал, что вы можете ссылаться на атрибуты элементов в обработчиках, просто упоминая их имя. Я всегда думал, что «событие» - единственное исключение.
Teemoh
1
Так как же сделать так, чтобы ваша страница не мигала, когда она все отображает заново? Кроме того, вы встраиваете основные стили оболочки приложения, чтобы ваша страница имела начальный макет вместо того, чтобы отображать то, чем Lynx мог бы гордиться?
Крис Лав
2
Кажется, у меня все еще работает. После этого я знаю, что больше получаю предупреждение в PageSpeed ​​Insights для этого файла.
AndyWarren
3
это работает для меня (08 июля-2018). И это дает хорошие результаты в Pagespeed Insight
abilash er 08
1
IMHO, новый ответ jabachetta с использованием «предварительной загрузки» , вероятно, является предпочтительным решением сейчас. Если у кого-то есть причина полагать, что этот ответ все же предпочтительнее, добавьте комментарий, объясняющий, почему. В идеале ссылка на ресурс, который подтверждает, почему / когда этот подход все еще может быть предпочтительным. [Предполагая, что вы используете полифилл для поддержки preloadFirefox и старых браузеров - см. Ссылку в первом комментарии к этому ответу].
ToolmakerSteve
109

Обновление 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, пока они не поддержат ключевое слово preload . И снова, как подробно описано в задокументированном ответе выше, предварительная загрузка на самом деле может не принести пользу.

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

Дополнительные соображения:

Обратите внимание, что в целом, если вы собираетесь загружать CSS асинхронно, обычно рекомендуется встраивать критический CSS , поскольку CSS не зря является ресурсом, блокирующим рендеринг .

Благодарим filament group за их многочисленные асинхронные CSS-решения.

Jabacchetta
источник
3
Нет необходимости использовать loadCSS, достаточно полифилла: github.com/filamentgroup/loadCSS/blob/master/src/…
nathan
1
Статус спецификации Preload, наконец, [Рекомендация кандидата W3C]. ( W3.org/TR/preload/… ) Firefox поддерживает его, но по-прежнему отключен по умолчанию; нажмите на ссылку "широко поддерживаемая" в ответе, чтобы получить описание флага Firefox, который управляет им, "Могу ли я использовать".
ToolmakerSteve
1
Ответ обновлен, чтобы предоставить решение для полной поддержки браузера, а также дополнительные пояснения.
jabacchetta
1
@jabacchetta, обновление 2020 года очень ценится, спасибо. Я специально искал инструкции, написанные в последнее время, где поддержка браузеров в целом лучше. Многое изменилось за 3 года в сети!
Брандито,
Видимо, так лучше 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 тоже стоит прочитать.

Flimm
источник
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

Подходы к асинхронной загрузке CSS

Есть несколько способов заставить браузер загружать CSS асинхронно, но ни один из них не настолько прост, как вы могли ожидать.

<link rel="preload" href="mystyles.css" as="style" onload="this.rel='stylesheet'">
Virender Nehra
источник
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 Pagespeed.

Согласно 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 : учитывая описание того, как они тестируются сейчас, и их поддержку тега « preload » для «link», ответ jabachetta, вероятно, сейчас лучший ответ для Google.
ToolmakerSteve
1
@ToolmakerSteve Да. Этот ответ нужно часто модерировать. Но этот код все еще работает, чтобы получить 100 в Page Speed.
Каушик Ганди
0

Я пытаюсь использовать:

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

Он отлично работает, но также увеличивает совокупный сдвиг макета, потому что, когда мы используем rel = "preload", он просто загружает css, а не применяется немедленно.

Пример, когда DOM загружает список, содержащий теги ul, li, по умолчанию перед тегами li есть маркеры, затем применен CSS, чтобы я удалил эти маркеры в пользовательские стили для листинга. Так что здесь происходит кумулятивный сдвиг макета.

Есть ли решение для этого?

n2lose
источник
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'">
Шамми Ханс
источник