
Может ли загрузка CSS-ресурсов блокировать парсинг страницы?
Прежде всего скажу, что на вопрос из заголовка этого раздела можно, без всякого сомнения, дать положительный ответ. Загрузка CSS-файлов способна не только заблокировать парсинг HTML-кода, но и не дать выполняться JavaScript-коду.
Для начала предлагаю поэкспериментировать. Для этого нам понадобится соответствующим образом настроить браузер. CSS-файл мы будем загружать с CDN, поэтому ограничим скорость работы с сетью в браузере Google Chrome. Для этого, на вкладке инструментов разработчика
Performance
, поменяем значение параметра
Network
на Slow 3G
. Исследовать будем
следующую страницу:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta data-fr-http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><link href="http://personeltest.ru/aways/cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet"><script>document.addEventListener('DOMContentLoaded', () => {console.log('DOMContentLoaded');})</script><script>console.log('script');Promise.resolve(1).then(res => {console.log('then');});</script></head><body><h1>hello</h1></body></html>
CSS-файл мы загружаем с CDN, при этом, так как скорость подключения к интернету искусственно ограничена, загрузка стилей займёт некоторое время. В результате до загрузки CSS-файла ничего не попадёт в консоль JavaScript и на экран не будет выведено содержимое страницы. То, что мы видим, указывает на то, что загрузка CSS блокирует загрузку и обработку других материалов страницы.

Вывод данных в JS-консоль
Может ли загрузка и выполнение JS-кода блокировать парсинг страницы?
Загрузка и обработка JS-файлов, безусловно, блокирует парсинг страницы. Но, чтобы исправить эту проблему, при подключении скриптов к странице можно пользоваться атрибутами
defer
и async
тега
<script>
. Сейчас мы изучим их воздействие на
загрузку страницы.Обычные загрузка и выполнение скрипта
Если в теге
<script>
не используются атрибуты
async
или defer
процесс загрузки и
обработки материалов страницы происходит так, как показано на
следующей схеме. Загрузка JS-файлов и выполнение содержащегося в
них кода блокирует парсинг HTML-кода.
Использование тега <script> без атрибутов async и defer
Здесь и далее мы будем пользоваться следующими цветовыми обозначениями.

HTML parsing Парсинг HTML; HTML parsing paused Парсинг HTML приостановлен; Script download Загрузка скрипта; Script execution Выполнение скрипта
Использование тега <script> с атрибутом async
Когда браузер обрабатывает тег
<script>
с
атрибутом async
, загрузка JavaScript-кода
осуществляется в асинхронном режиме. Код скрипта выполняется сразу
после загрузки. При этом выполнение JS-кода блокирует парсинг
HTML.
Использование тега <script> с атрибутом async
Использование тега <script> с атрибутом defer
Если в теге
<script>
имеется атрибут
defer
код скрипта загружается асинхронно. При этом
код, после завершения его загрузки, выполняется только тогда, когда
будет завершён парсинг HTML-кода.
Использование тега <script> с атрибутом defer
Эксперименты
Давайте поэкспериментируем с атрибутами
async
и
defer
. Начнём со следующей страницы:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta data-fr-http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>DomContentLoaded</title></head><body><script src="http://personeltest.ru/away/code.jquery.com/jquery-1.4.4.min.js"></script><script src="./index.js"/> // 0<script src="./index2.js"/> // 2<script >console.log('inline');Promise.resolve().then(res=>{console.log('then');})</script><div id="hello">hello world</div><script>document.addEventListener('DOMContentLoaded', () => {console.log('DOMContentLoaded');})</script></body></html>
Эта страница, помимо загрузки скрипта
jquery-1.4.4.min.js
с CDN, загружает пару собственных
скриптов index.js
и index2.js
. Ниже
приведён их код.Файл
index.js
:
Promise.resolve().then((res) => {console.log('index1');return res;});
Файл
index2.js
:
Promise.resolve().then((res) => {console.log('index2');return res;});
В ходе загрузки этой страницы в JS-консоль попадает то, что показано ниже.

Вывод данных в JS-консоль
В результате у нас есть доказательство того, что загрузка и обработка JS-файлов блокирует рендеринг HTML-кода. Сообщения, выводимые скриптами, появляются в консоли до сообщения, указывающего на завершение загрузки содержимого DOM.
Теперь посмотрим на то, как ведут себя скрипты, в тегах
<script>
которых используется атрибут
<async>
:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta data-fr-http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>DomContentLoaded</title></head><body><script async src="http://personeltest.ru/away/code.jquery.com/jquery-1.4.4.min.js"></script><script src="./index.js"></script><script src="./index2.js"/></script><script>console.log('inline');Promise.resolve().then(res=>{console.log('then');})</script><div id="hello">hello world</div><script>document.addEventListener('DOMContentLoaded', () => {console.log('DOMContentLoaded');})</script></body></html>
Изучим то, что попадёт в консоль.

Вывод данных в JS-консоль
Скрипт библиотеки jQuery загружается асинхронно. То, что попадает в консоль, выводится там до его загрузки. Если скрипт библиотеки загружается слишком медленно это не помешает парсингу HTML-кода. Сообщение
DOMContentLoaded
может быть выведено и до, и
после завершения загрузки и выполнения async-скрипта. А при
применении атрибута defer
скрипт будет загружен
асинхронно, дождётся завершения обработки материалов документа, а
потом, но до события DOMContentLoaded
, будет
выполнен.Если вам интересна тема оптимизации веб-страниц с учётом возможной блокировки обработки их кода при загрузке внешних стилей возможно, вам стоит взглянуть на этот материал.
Сталкивались ли вы с проблемами, связанными с блокировкой обработки материалов веб-страниц?