Часто для реализации какого-либо конфигурируемого функционала на фронтенде необходимо передать параметры конфигурации из бэкенда на фронтенд. Для этого используется множество неоптимальных подходов.
Как это обычно делают? Подключают вот такой вот 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-код, браузер переключается на его выполнение и только после этого продолжает отрисовывать страницу. Этот эффект легко заметить на медленных устройствах — контент отрисовывается частями от скрипта к скрипту.
Что же можно улучшить в этой ситуации? Итак:
Давайте посмотрим, как это будет выглядеть:
<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-объекта конфигурации, улучшаем организационную структуру и читабельность кода.
Повторим принципы, которые были реализованы этим подходом:
JSON.parse
, чтобы не блокировать рендеринг страницы и onLoad-событие.Как по мне, не так уж и сложно реализовать правильное взаимодействие бэкенда и фронтенда, если понимать, какой подход для этого правильно использовать.
Это текст из личного блога, опубликованный с разрешения автора.
В благословенные офисные времена, когда не было большой войны и коронавируса, люди гораздо больше общались…
Вот две истории из собственного опыта, с тех пор, когда только начинал делать свою карьеру…
«Ты же программист». За свою жизнь я много раз слышал эту фразу. От всех. Кто…
Отличные новости! Если вы пропустили, GitHub Copilot — это уже не отдельный продукт, а набор…
Несколько месяцев назад мы с командой Promodo (агентство инвестировало в продукт более $100 000) запустили…
Пару дней назад прочитал сообщение о том, что хорошие курсы могут стать альтернативой классическому образованию.…