Недавно мне попался материал, посвящённый проблеме загрузки CSS-файлов, которая замедляет обработку материалов страниц. Я читал ту статью, стремясь научиться чему-то новому, но мне показалось, что то, о чём там говорилось, не вполне соответствует истине. Поэтому я провёл собственное исследование этой темы и поэкспериментировал с загрузкой CSS и JavaScript.
Прежде всего скажу, что на вопрос из заголовка этого раздела можно, без всякого сомнения, дать положительный ответ. Загрузка 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="https://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-файлов, безусловно, блокирует парсинг страницы. Но, чтобы исправить эту проблему, при подключении скриптов к странице можно пользоваться атрибутами defer и async тега <script>. Сейчас мы изучим их воздействие на загрузку страницы.
Если в теге <script> не используются атрибуты async или defer — процесс загрузки и обработки материалов страницы происходит так, как показано на следующей схеме. Загрузка JS-файлов и выполнение содержащегося в них кода блокирует парсинг HTML-кода.
Использование тега <script> без атрибутов async и defer
Здесь и далее мы будем пользоваться следующими цветовыми обозначениями.
HTML parsing — Парсинг HTML; HTML parsing paused — Парсинг HTML приостановлен; Script download — Загрузка скрипта; Script execution — Выполнение скрипта
Когда браузер обрабатывает тег <script> с атрибутом async, загрузка JavaScript-кода осуществляется в асинхронном режиме. Код скрипта выполняется сразу после загрузки. При этом выполнение JS-кода блокирует парсинг HTML.
Использование тега <script> с атрибутом async
Если в теге <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://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://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, будет выполнен.
Если вам интересна тема оптимизации веб-страниц с учётом возможной блокировки обработки их кода при загрузке внешних стилей — возможно, вам стоит взглянуть на этот материал.
Источник статьи: https://habr.com/ru/company/ruvds/blog/560740/
Может ли загрузка 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="https://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://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://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, будет выполнен.
Если вам интересна тема оптимизации веб-страниц с учётом возможной блокировки обработки их кода при загрузке внешних стилей — возможно, вам стоит взглянуть на этот материал.
Источник статьи: https://habr.com/ru/company/ruvds/blog/560740/