Ось що відбувається, коли браузер завантажує веб-сайт із <script>
тегом на ньому:
- Виберіть сторінку HTML (наприклад, index.html)
- Почніть розбирати HTML
- Аналізатор стикається з
<script>
тегом, що посилається на зовнішній файл сценарію.
- Браузер запитує файл сценарію. Тим часом, аналізатор блокує та зупиняє розбір іншого HTML на вашій сторінці.
- Через деякий час сценарій завантажується і згодом виконується.
- Аналізатор продовжує розбирати решту HTML-документа.
Крок №4 спричиняє погану роботу користувачів. В основному ваш веб-сайт припиняє завантаження, поки ви не завантажите всі сценарії. Якщо є одна річ, яку ненавидять користувачі, вона чекає завантаження веб-сайту.
Чому це взагалі трапляється?
Будь-який сценарій може вставити свій власний HTML за допомогою document.write()
або інших маніпуляцій DOM. Це означає, що аналізатору доводиться чекати, поки сценарій буде завантажений і виконаний, перш ніж він зможе безпечно проаналізувати решту документа. Зрештою, сценарій міг би вставити власний HTML в документ.
Однак більшість розробників JavaScript більше не маніпулює DOM під час завантаження документа. Натомість вони чекають, поки документ буде завантажений, перш ніж його змінити. Наприклад:
<!-- index.html -->
<html>
<head>
<title>My Page</title>
<script src="my-script.js"></script>
</head>
<body>
<div id="user-greeting">Welcome back, user</div>
</body>
</html>
Javascript:
// my-script.js
document.addEventListener("DOMContentLoaded", function() {
// this function runs when the DOM is ready, i.e. when the document has been parsed
document.getElementById("user-greeting").textContent = "Welcome back, Bart";
});
Оскільки ваш браузер не знає my-script.js не збирається змінювати документ до його завантаження та виконання, аналізатор припиняє розбір.
Старовинна рекомендація
Старий підхід до вирішення цієї проблеми полягав у тому, щоб помістити <script>
теги внизу вашого <body>
, оскільки це забезпечує те, що аналізатор не буде заблокований до самого кінця.
У цього підходу є своя проблема: браузер не може почати завантаження скриптів, поки не буде розбір всього документа. Для великих веб-сайтів з великими сценаріями та таблицями стилів дуже важливою є можливість завантаження сценарію якнайшвидше. Якщо ваш веб-сайт не завантажиться протягом 2 секунд, люди перейдуть на інший веб-сайт.
В оптимальному рішенні браузер почне завантажувати ваші сценарії якнайшвидше, в той же час аналізуючи решту вашого документа.
Сучасний підхід
Сьогодні браузери підтримують сценарії async
та defer
атрибути. Ці атрибути говорять браузеру про безпечне продовження розбору під час завантаження скриптів.
асинхронізація
<script src="path/to/script1.js" async></script>
<script src="path/to/script2.js" async></script>
Сценарії з атрибутом async виконуються асинхронно. Це означає, що сценарій виконується відразу після його завантаження, не блокуючи браузер тим часом.
Це означає, що сценарій 2 завантажується та виконується перед сценарієм 1.
За даними http://caniuse.com/#feat=script-async , 97,78% усіх браузерів підтримують це.
відкладати
<script src="path/to/script1.js" defer></script>
<script src="path/to/script2.js" defer></script>
Сценарії з атрибутом відстрочки виконуються в порядку (тобто спочатку сценарій 1, потім сценарій 2). Це також не блокує браузер.
На відміну від сценаріїв асинхронізації, сценарії відстрочки виконуються лише після завантаження всього документа.
За даними http://caniuse.com/#feat=script-defer , 97,79% усіх браузерів підтримують це. 98,06% підтримують її хоча б частково.
Важлива примітка щодо сумісності браузера: в деяких обставинах IE <= 9 може виконувати відкладені сценарії не в порядку. Якщо вам потрібна підтримка цих браузерів, будь ласка, прочитайте це спочатку!
Висновок
Поточний стан сучасності полягає в тому, щоб помістити сценарії в <head>
тег і використовувати атрибути async
або defer
. Це дозволяє завантажувати ваші сценарії якнайшвидше, не блокуючи браузер.
Хороша річ у тому, що ваш веб-сайт все-таки повинен правильно завантажувати 2% браузерів, які не підтримують ці атрибути, прискорюючи інші 98%.