Сучасна асинхронне завантаження CSS

358

Від автора: як зробити асинхронну завантаження файлів CSS без використання JS. Використання атрибуту media і веб-стандарту preload — про це у статті.

Найпростіший спосіб завантажити CSS файл в HTML – зробити це через тег link і rel=»stylesheet»:

Відмінний спосіб, але у нього є свій недолік: він синхронний. Іншими словами, браузер зупиняє рендер всього, що розташована під рядком підключення файлу, до тих пір, поки файл не буде распарсен. Іноді це необхідно, якщо ми не хочемо, щоб браузер рендерил сторінку завантаження CSS. Але не всі файли CSS критичні настільки, щоб затримувати доступ до контенту. Саме тому нам потрібна асинхронне завантаження CSS, ми настійно рекомендуємо встановлювати пріоритети і оптимізувати файли для швидкої і надійної доставки.

Щоб некритичні CSS файли не блокували рендер сторінки, їх потрібно завантажувати асинхронно.

Способи асинхронної завантаження CSS

Існує кілька способів змусити браузер асинхронно завантажувати CSS, і всі вони досить складні.

Один із способів (працює в сучасних браузерах) – з допомогою JS створювати і вставляти посилання на стилі DOM:

// make a link stylesheet
var myCSS = document.createElement( “link” );
myCSS.rel = “stylesheet”;
myCSS.href = “mystyles.css”;
// insert it at the end of the head in a legacy-friendly manner
document.head.insertBefore( myCSS, document.head.childNodes[ document.head.childNodes.length – 1 ].nextSibling );

Останній рядок чудова!

Ще один спосіб – встановити атрибут media в тезі link в медіа тип (або запит), який не збігається з браузером користувача. Наприклад, media=»print» або взагалі щось нераспознаваемое типу media=»nope!». Стилі незастосовні медіа браузери ставлять у нижчий пріоритет і завантажують їх, не блокуючи рендер сторінки. Добре, але щоб відобразити їх після завантаження, нам необхідно з допомогою JS обробника події onload змінювати значення media на значення, що збігається з браузером користувача, наприклад, screen або all:

Примітка: ми використовуємо комбінацію трюків, описаних вище, в нашій бібліотеці loadCSS.js для асинхронної завантаження CSS. Також там є хакі для старих, але все ще діючих браузерів, які не підтримують події onload на елементах link.

Як і в методі перемикання медіа, ми можемо асинхронно завантажувати CSS, якщо пометим link, як alternate стилі пропонують альтернативні подання сайту), і потім з допомогою JS перемкнемо атрибут rel назад в stylesheet після завантаження файла:

Методи вище працюють, але у всіх у них є недолік – всі вони використовують JS (не кажучи вже, що потрібно знати нюанси поведінки браузерів), досягаючи бажаного ефекту не прямим способом…

Сучасний підхід

На щастя, зараз є веб-стандарт, спроектований спеціально для асинхронної завантаження ресурсів типу CSS — rel=»preload». Нарешті-то ми можемо асинхронно завантажувати CSS без JS! Жартую. Може, це вас здивує, але навіть цей метод використовує обробник події onload. Але це наш кращий варіант.

Приклад асинхронної завантаження і застосування CSS через rel=»preload» (в браузер з підтримкою):

Як і методи з перемиканням атрибута rel=»preload» змушує браузери з підтримкою завантажувати, але не застосовувати файли. Тому нам потрібен обробник події onload для установки rel значення stylesheet. Можливо ви не бачите сильних поліпшень в порівнянні з іншими методами, однак rel=»preload» змушує браузери завантажувати файл раніше, ніж, наприклад, у способі з несовпадающим медіа типом.

Використання rel=preload разом з loadCSS

Підтримка браузерами rel=»preload» становить… ну, в загальному, хоча б Chrome підтримує. Інші основні браузери теж незабаром будуть підтримувати це значення. У Firefox 56 вже є підтримка, але там дуже багато помилок (preload працює тільки для файлів, які явно вважаються кешованими), тому в Firefox 57 цю функцію відключили (підтримка повернеться в Firefox 59).

На щастя, ми можемо протестувати це значення і використовувати полифилы для роботи rel=»preload» у всіх браузерах. Наш проект loadCSS пропонує скрипт cssrelpreload.js, який включає підтримку rel=»preload» для CSS файлів в браузерах, в яких немає рідної підтримки (скрипт нічого не робить, якщо браузер знає значення preload).

Можете знайти покрокову інструкцію для використання cssrelpreload.js в readme проекту. Якщо ви підключите скрипт до сторінки инлайново (або через server-push), це автоматично змусить працювати елементи link[rel=”preload”] в браузерах без підтримки. Приклад використання, в тому числі і фолбек noscript на всяк випадок:

/*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */ (function(){ … }());

Примітка: якщо ви вже працювали з cssrelpreload.js ,то в останній версії 2.0.1 є пара поліпшень, і бібліотека більше не залежить від скрипта loadCSS.js. Тому більше не можете підключати цей файл і заощадити пару кілобайт.

Як і будь-open source проект, ви можете знайти loadCSS на Github і на NPM. Якщо у вас виникли проблеми, є питання або коментарі, пишіть в issue tracker. Дякую!