Missed the action at the 2018 Chrome Dev Summit? Catch up with our playlist on the Google Chrome Developers channel on YouTube. Watch now.

Оптимизация JavaScript для быстрой визуализации страницы

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

TL;DR

  • JavaScript может изменять DOM и CSSOM, а также получать данные из этих моделей.
  • Выполнение JavaScript невозможно без модели CSSOM.
  • JavaScript препятствует созданию DOM, если не использовать асинхронизацию.

JavaScript - это динамический язык, с помощью которого можно менять почти любые характеристики веб-страниц: модифицировать контент, добавляя элементы в модель DOM или удаляя их; менять CSSOM-свойства любых элементов; отвечать на действия пользователя и т. д. Рассмотрим эти возможности, дополнив прошлый пример Hello World встроенным кодом:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet">
    <title>Critical Path: Script</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg"></div>
    <script>
      var span = document.getElementsByTagName('span')[0];
      span.textContent = 'interactive'; // change DOM text content
      span.style.display = 'inline';  // change CSSOM property
      // create a new element, style it, and append it to the DOM
      var loadTime = document.createElement('div');
      loadTime.textContent = 'You loaded this page on: ' + new Date();
      loadTime.style.color = 'blue';
      document.body.appendChild(loadTime);
    </script>
  </body>
</html>
  • JavaScript позволяет извлечь из DOM скрытый объект span, который может не отображаться в модели визуализации, а затем заменить текст внутри объекта (с помощью команды .textContext) и вычисляемое свойство стиля (с none на inline). Готово! На странице появится текст Hello interactive students!.

  • Кроме того, JavaScript позволяет создавать новые объекты в модели DOM, менять их стиль, а также присоединять их к модели и удалять. То есть мы могли бы создать полноценную веб-страницу, прописав все элементы и их стили в файле JavaScript, однако это намного проще осуществить с помощью HTML и CSS. Обратите внимание, что во второй части примера мы создаем блок div, добавляем в него текст, задаем стиль и присоединяем блок к основному коду.

вид страницы

Итак, мы изменили контент и стиль одного объекта в модели DOM, а также добавили в документ новый объект. И хотя наша страница вряд ли получит приз Дизайн года, она прекрасно иллюстрирует возможности и гибкость JavaScript.

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

Например, обратите внимание, что в приведенном выше примере встроенный код добавлен в конец страницы. Почему? Все просто: если мы поместим скрипт над тегом span, его невозможно будет выполнить. Функция getElementsByTagName('span') не сможет обнаружить в документе элементов span и вернет результат null. Из этого можно сделать важный вывод: скрипты выполняются именно в той последовательности, в которой они представлены в документе. Когда синтаксический анализатор встречает тег скрипта, он приостанавливает создание модели DOM и запускает механизм выполнения JavaScript. Создание модели продолжится только после того, как механизм завершит работу.

Получается, что скрипт не может обнаружить расположенные после него элементы, потому что они ещё не обработаны. Кроме того, при выполнении встроенного скрипта создание DOM прекращается, что замедляет визуализацию страницы.

При использовании скриптов также следует помнить, что они могут изменять не только свойства DOM, но и CSSOM. Именно это и происходит в нашем примере при замене значения none на inline в свойствах стиля. В результате образуется состояние гонки.

Представьте, что к тому моменту, когда нужно запустить скрипт, браузер ещё не закончил создание CSSOM. Это приведет к тому, что браузер будет задерживать выполнение скрипта до тех пор, пока не построит CSSOM. В это время создание DOM будет приостановлено, из-за чего скорость визуализации существенно снизится.

Поэтому учтите, что из-за взаимосвязей между DOM, CSSOM и кодом JavaScript выполнение скриптов может вызвать задержку в обработке и визуализации страницы.

  1. Расположение скрипта в документе имеет значение.
  2. При обнаружении тега скрипта создание DOM останавливается до тех пор, пока скрипт не будет выполнен.
  3. JavaScript может получать данные из DOM и CSSOM, а также изменять их.
  4. Выполнение скрипта откладывается до тех пор, пока не загрузится CSSOM.

Чтобы ускорить визуализацию, нужно понять взаимосвязь между файлами HTML, CSS и JavaScript и оптимизировать ее. Это и есть оптимизация визуализации.

Блокировка анализатора и асинхронный JavaScript

Как вы помните, встретив в документе JavaScript, браузер приостанавливает создание DOM и возобновляет его только после выполнения скрипта. Другими словами, JavaScript блокирует работу синтаксического анализатора, что видно на примере со встроенным кодом. Блокировку можно предотвратить с помощью дополнительного кода.

Но что если поместить скрипт не в HTML-код, а в отдельный файл, и сослаться на него с помощью script-тега? Например, вот так:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet">
    <title>Critical Path: Script External</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg"></div>
    <script src="app.js"></script>
  </body>
</html>

app.js

var span = document.getElementsByTagName('span')[0];
span.textContent = 'interactive'; // change DOM text content
span.style.display = 'inline';  // change CSSOM property
// create a new element, style it, and append it to the DOM
var loadTime = document.createElement('div');
loadTime.textContent = 'You loaded this page on: ' + new Date();
loadTime.style.color = 'blue';
document.body.appendChild(loadTime);

Изменится ли порядок анализа при использовании тега <script> вместо встроенного кода? Конечно, нет. Синтаксический анализ будет производиться в том же порядке: браузер приостановит создание DOM для выполнения скрипта. Однако в этот раз браузеру также нужно дождаться загрузки JavaScript-файла с диска, удаленного сервера или из кеша, поэтому визуализация страницы задержится на десятки или даже тысячи милисекунд!

Но паниковать не стоит. Мы можем избежать этой ситуации и предотвратить блокировку анализатора, подав браузеру сигнал о том, что скрипт JavaScript можно выполнить позже. Браузер сможет создать DOM и начнет выполнять скрипт только тогда, когда он будет полностью загружен из кеша или с удаленного сервера.

Для этого нужно просто добавить в тег скрипта ключевое слово async:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet">
    <title>Critical Path: Script Async</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg"></div>
    <script src="app.js" async></script>
  </body>
</html>

По нему браузер определит, что блокировать создание DOM на время загрузки скрипта не нужно. Результат - высокая скорость визуализации!