JS: Архитектура фронтенда
Теория: Инициализация приложения
Работу любого приложения можно условно разделить на три стадии, через которые оно проходит в процессе жизни:
- Инициализацию
- Исполнение
- Завершение
В зависимости от того, как они реализованы, код может быть как удобным в тестировании и поддержке, так и наоборот, сложным и практически не тестируемым. В этом уроке мы рассмотрим самую главную часть — различия между инициализацией и исполнением, разберем конкретные примеры и научимся правильно разделять ответственности.
Что такое инициализация? Перед работой любого приложения нужно настроить все необходимые библиотеки и фреймворки, соединить их между собой и запустить. В бекенде с этим все сильно проще, так как этот процесс полностью контролируется фреймворками. Во фронтенде многое отдано на откуп самому разработчику, и поэтому часто возникают ситуации, когда процесс инициализации либо не выделен, либо выделен лишь частично и неудачно.
Что входит в инициализацию? Все, что нужно сделать ровно один раз для последующего использования в приложении:
- Создание начального состояния
- Настройка i18next
- Загрузка и запуск фреймворка, если он есть
- Подключение и настройка различных библиотек: http-клиенты, вебсокеты, работа с датами и так далее
Этот список далеко не полный, в каждой конкретной ситуации в инициализацию попадает что-то свое.
На практике процесс инициализации организуется так: создается функция, внутри которой выполняется вся необходимая настройка. Для удобства сделать это можно в файле init.js.
Запуск этой функции происходит уже в другом месте, например, в файле index.js, который является точкой входа в приложение:
Почему разделение именно такое? Во фронтенде приложение, как правило, глобально. То есть оно одно на всю страницу, грузится ровно один раз и один раз запускается и управляет всем происходящим так, как будто вокруг больше ничего нет (других приложений, о которых нужно думать). Но так бывает не всегда. Например, виджеты могут появляться на одной странице более одного раза, а значит каждый виджет должен быть вещью «в себе». То есть инициализация такого виджета работает в своем собственном окружении со своими собственными объектами и не меняет ничего вне (глобальные объекты). Иначе возникнут конфликты, и один виджет будет мешать другому.
С другой стороны, в тестировании каждый тест строится так, что он не зависит от других тестов, то есть каждый тест работает так, как будто других тестов не существует. Такое поведение требует инициализации приложения для каждого теста «с нуля». Только в этом случае можно гарантировать, что изменения состояния приложения, сделанные в одном тесте не повлияют на другие тесты. Яркий пример — инициализация i18next. Эта библиотека экспортирует глобальный объект, который можно инициализировать только один раз, повторные инициализации того же объекта (например, при повторных запусках приложения в тестах) приведут к багам и запрещены документацией. По этой причине в примере выше каждый старт приложения создает свой собственный экземпляр i18next, который затем прокидывается в использующие его функции.
В примере выше работа идет с функцией, которая позволяет инициализировать приложение, а не с файлом index.js, где эта функция вызывается. Если бы мы импортировали index.js, то во время импорта сразу произошла бы инициализация и она была бы глобальной с точки зрения тестов. В таком случае нормальное тестирование практически невозможно.




