Часто для реализации какого-либо конфигурируемого функционала на фронтенде необходимо передать параметры конфигурации из бэкенда на фронтенд. Для этого используется множество неоптимальных подходов.
Простой подход
Как это обычно делают? Подключают вот такой вот js-скрипт в head-блок:
<html> <head> <!-- блокировка рендеринга страницы на время выполнения скрипта --> <script> const configuration = <?php echo json_encode($configuration) ?>; </script> <!-- блокировка рендеринга страницы на время выполнения скрипта --> <script> //какое-то использование конфигурации в head window.console.log(configuration); </script> <!-- блокировка рендеринга страницы на время загрузки и выполнения скрипта --> <script src="/app-configuration-initialization.js"></script> <body> <!-- блокировка рендеринга страницы на время выполнения скрипта --> <script> //какое-то использование конфигурации в body window.console.log(configuration); </script> </body> </html>
Дальше глобальную константу configuration
можно будет использовать как в подключаемых js-файлах, так и в js-коде в теле самой страницы, на которой мы подключили этот блок.
Казалось бы, все хорошо. Удобно конфигурировать и удобно использовать. Но есть в таком подходе ряд проблем с производительностью.
Браузер выполняет отрисовку страницы и скрипты в одном потоке. То есть встретив в теле страницы js-код, браузер переключается на его выполнение и только после этого продолжает отрисовывать страницу. Этот эффект легко заметить на медленных устройствах — контент отрисовывается частями от скрипта к скрипту.
Часто используемый подход
Что же можно улучшить в этой ситуации? Итак:
- во-первых, подключить внешний js-скрипт без блокировки рендеринга страницы;
- во-вторых, опустить конфигурацию и весь js-код, использующий ее, как можно ниже.
Давайте посмотрим, как это будет выглядеть:
<html> <head> <!-- загрузка скрипта теперь не блокирует рендеринг страницы --> <!-- скрипт будет выполнен после инициализации всей страницы --> <!-- не страница ждет скрипт, а скрипт страницу --> <script defer src="/app-configuration-initialization.js"></script> <body> <!-- блокировка рендеринга страницы на время выполнения скрипта --> <script> const configuration = <?php echo json_encode($configuration) ?>; </script> <!-- блокировка рендеринга страницы на время выполнения скрипта --> <script> //какое-то использование конфигурации в head window.console.log(configuration); </script> <!-- блокировка рендеринга страницы на время выполнения скрипта --> <script> //какое-то использование конфигурации в body window.console.log(configuration); </script> </body> </html>
Казалось бы, все стало идеально. Рендеринг страницы ничего не блокирует. Она полностью отрисовывается, потом происходит инициализация конфигурации и выполняются все скрипты, использующие ее.
Каждому свое место
Но не все так хорошо, как кажется. Из-за того, что js-код присутствует в коде страницы, не происходит событие onLoad
, до которого желательно никакие скрипты не выполнять. Js-код нужно писать в js-файлах и подключать как внешний ресурс.
Итак, выносим весь js-код, который можем, во внешние скрипты:
<html> <head> <!-- загрузка скрипта теперь не блокирует рендеринг страницы --> <!-- скрипт будет выполнен после инициализации всей страницы --> <!-- не страница ждет скрипт, а скрипт страницу --> <!-- в этом же скрипте теперь находится весь код использующий конфигурацию --> <script defer src="/app-configuration-initialization.js"></script> <body> <!-- блокировка рендеринга страницы на время выполнения скрипта --> <script> const configuration = <?php echo json_encode($configuration) ?>; </script> </body> </html>
Стало намного лучше, js-код инициализации конфигурации все еще блокирует окончание загрузки страницы.
Отложить на потом не так уж и плохо
Как оказалось, использовать JSON-объект для инициализации конфигурации очень дорого и лучше использовать для этого JSON-строку, которую можно будет распарсить при инициализации нашего внешнего js-скрипта, используя нативный метод JSON.parse()
.
Подробнее и с примерами можно посмотреть в видео:
Совет от Google, конечно, интересный и хороший, но всегда же можно сделать лучше.
Есть у тега скрипт — возможность задать его тип. И есть такой тип как application/JSON. Его особенность в том, что браузер не парсит его содержимое во время рендеринга страницы и попросту пропускает такие теги, как вроде бы их и нет вовсе. Но при этом мы можем получить содержимое этих тегов в виде текста в нашем js-скрипте и распарсить уже известным нам методом, предложенным в видео.
В итоге у нас получается вот такой код:
<html> <head> <!-- загрузка скрипта теперь не блокирует рендеринг страницы --> <!-- скрипт будет выполнен после инициализации всей страницы --> <!-- не страница ждет скрипт, а скрипт страницу --> <!-- в этом же скрипте теперь находится весь код использующий конфигурацию --> <!-- в этом же скрипте теперь происходит инициализация конфигурации--> <script defer src="/app-configuration-initialization.js"></script> <body> <!-- скрипт не блокирует рендеринг страницы и не выполняется --> <script type="application/json" id="app-configuration-json"> <?php echo json_encode($configuration) ?>; </script> </body> </html>
Получаем страницу, на которой отсутствует js-код, блокирующий отрисовку и полную загрузку страницы. То, чего нужно было добиться. При этом весь js-код, в том числе и инициализацию конфигурации, мы можем написать во внешнем js-скрипте, где ему и место.
Итоги
Используя описанный подход, мы экономим трафик (внешние скрипты кешируются, а код страницы стал на много меньше), не блокируем рендеринг страницы, не блокируем onLoad-событие, экономим ресурсы процессора, так как используем JSON.parse
вместо JSON-объекта конфигурации, улучшаем организационную структуру и читабельность кода.
Повторим принципы, которые были реализованы этим подходом:
- внешние ресурсы нужно подключать асинхронно, чтобы не блокировать рендеринг страницы;
- использовать js-скрипты в теле страницы нельзя, чтобы не блокировать рендеринг страницы;
- передавать конфигурацию лучше всего через script-тег с типом application/JSON и парсить ее при инициализации с помощью
JSON.parse
, чтобы не блокировать рендеринг страницы и onLoad-событие.
Как по мне, не так уж и сложно реализовать правильное взаимодействие бэкенда и фронтенда, если понимать, какой подход для этого правильно использовать.
Это текст из личного блога, опубликованный с разрешения автора.
Этот материал – не редакционный, это – личное мнение его автора. Редакция может не разделять это мнение.
Сообщить об опечатке
Текст, который будет отправлен нашим редакторам: