Манипулирование DOM — задача простая только в самых примитивных ситуациях. Как только понадобится реализовать полноценное (пусть и небольшое) приложение, код моментально превращается в тыкву. Десятка обработчиков достаточно для того, чтобы потеряться. С каждым новым событием сложность кода растет еще быстрее, а ведь в реальных приложениях событий сотни. Почему так происходит?
Хотя подобная проблема касается не только фронтенда, именно в нем она достигает своего апогея. Событийная архитектура и DOM без должного внимания порождают запутанный код буквально сразу. Понятно, что где-то здесь появляется архитектура, но где конкретно и как — это вопрос.
Подойдем к правильной архитектуре со стороны бэкенда. Как вы уже знаете или догадываетесь, в бэкенде приложения состоят минимум из двух частей — базы данных и собственно самого кода. Формы, отправляемые на сервер, изменяют состояние приложения (то есть его данные), которое хранится в базе, далее на основе этого состояния формируется ответ в виде HTML-страницы.
По сути, в типичных веб-проектах приложение занимается двумя процессами: либо обновляет данные в базе, либо извлекает эти данные и на основе них формирует HTML. Необходимость базы данных довольно очевидна и понятна для всех, но то же самое не очевидно во фронтенде. DOM позволяет хранить состояние внутри себя и, более того, провоцирует так делать. На протяжении курса мы будем встречаться с этой ситуацией еще не раз, когда нужно выполнить некоторое действие, а оно зависит от того, что сейчас на экране. Для работы с этими данными приходится обращаться к DOM.
See the Pen js_dom_state_in_dom by Hexlet (@hexlet) on CodePen.
Главная проблема такого подхода в отсутствии единого источника правды. Одни и те же данные, на одной странице, могут использоваться множество раз. Причем иногда по-разному. Представьте себе ситуацию, в которой нужно вывести количество отображаемых статей на странице. Для того чтобы их посчитать, придется писать такой код:
// Каждая статья обозначается классом .article
const articles = document.querySelectorAll('.article');
articles.length; // количество статей
А если у нас часть статей выводится в одном блоке, а часть в другом? Тогда придется обращаться к каждому из этих блоков, чтобы собрать все статьи. Чем дальше мы будем фантазировать, тем больше сложностей возникнет с тем, как работать с этими данными: добавлять, обновлять и использовать. Глядя на код, невозможно легко понять, откуда брать данные. Кроме того, они сильно завязаны на конкретную структуру верстки.
Отделение состояния приложения
Первый шаг в построении правильной архитектуры состоит в выделении данных, которыми манипулирует приложение из DOM. Их необходимо выделить в отдельную переменную на уровне приложения (не на глобальном уровне! Каждый запуск приложения должен быть независим). Это позволит отделить работу с данными от их отображения.
При такой организации кода вырисовывается следующая схема работы:
- Возникает событие
- Обработчик события меняет состояние
- DOM обновляется на основе новых данных
Ниже реализация этой идеи на примере простого счетчика. Состояние в данном случае — одно число. Кнопка с плюсом увеличивает его на единицу, кнопка с минусом соответственно уменьшает.
See the Pen js_dom_state_simple by Hexlet (@hexlet) on CodePen.
Здесь нет никаких обращений к DOM для извлечения текущего значения, оно хранится в переменной и доступно для всех обработчиков. Подобная организация упрощает не только хранение и работу с данными, но и отладку. В любой момент можно посмотреть внутрь состояния и сопоставить его с тем что выводится на экран. Фактически, внешний вид страницы становится отражением состояния приложения на экране.
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.