Эксперимент. Корзина для minishop2 на реакте.
Всем привет! Сегодня я затеял небольшой эксперимент с неоднозначным результатом. В общем, я решил сделать фронтовый модуль корзины для минишопа, чтобы в фронте все аджаксово и реатово было, но не просто так, а чтобы какая-то внешняя система плагинов была, то есть чтобы можно было вклиниться в логику работу модуля и отрисовывать элементы со своими собственными шаблонами. Чессказать, не думал, что без вебпака (сборщика) все так сложно и неудобно будет. Короче, в итоге потратил 13 часов вместо предполагаемых трех-четырех. Сложно передать словами все, с чем я столкнулся и сколько всего неудобств вытерпел, но не буду особо вдаваться в это, а просто покажу что было и что стало. Для теста сайт любезно предоставил Михаил Воеводский, за что ему большое спасибо. Вот оригинальный сайт: http://old.demo1.minishop.dev.modx.site/ Покликайте его, посмотрите как работает корзина, ведь все познается в сравнении. Обратите внимание, что на странице просмотра заказа выводится сразу два блока корзины, и как раз здесь всплывает проблема, которую хотелось бы решить - когда меняешь кол-во товаров в одном блоке, в другом они не обновляются. Собственно, реакт как раз такие моменты и решает с легкостью. Но я-то сегодня не ищу легких путей, я хотел, чтобы это дружило с Жучкой, чтобы не приходилось осваивать вебпак и т.п., не погружаться глубоко в javascript и прочие дебри, а просто можно было подставить свой шаблончик для рендеринга корзины. Собственно, частично я эту задачу решил. Можете посмотреть на обновленном сайте: http://demo1.minishop.dev.modx.site/ Я убрал пока блок непосредственного оформления заказа, для эксперимента это было бы уже слишком. Так же предупреждаю, что я не во всех браузерах тестировал, но должно работать в хроме и огнелисе. Покликали? Давайте тогда разберем зачем все это затевалось и что это дает. Самое главное что мы здесь получается - совершенно иной механизм работы веб-интерфейса. В классическом варианте что происходит когда вы, к примеру, хотите изменить количество товаров в определенной позиции заказа? Вы кликаете элемент +/- или вручную меняете число в текстовом поле, после чего значение меняется, данные отправляются на сервер и от него вы получается ответ чаще всего однозначный и довольно простой, типа "Корзина успешно пересчитана" или "Корзина не была пересчитана". Вот когда все успешно, тогда еще боле менее ОК. А если нет? Надо восстановить значение текстового поля. А если их несколько (как это на оригинальном представленном сайте)? А если товарная позиция сложносоставная, с зависимыми позициями, начислениями скидок и т.п.? Тут и без ошибок-то не легко все элементы на странице учесть и актуализировать, а в случае ошибки вообще тьма. А что мы получаем с реактом? С реактом у нас другая ситуация: у нас есть единое хранилище состояния заказа и все элементы на странице отрисовываются от этого состояния. В таком случае не надо следить за тем кто что кликнул и где что изменилось. Главное - следить за состоянием объектом состояния, а все элементы зависимые от него сами перерисуются с нужными значениями. При таком подходе интерфейсы сильно завязаны на основном модуле и становятся как заколдованные. Вот посмотрите этот ролик. Как бы я не менял значения полей, они сразу восстанавливаются, потому что заказ при этом не был изменен. Но в конце вы можете заметить, что одна позиция изменила количество и сумму, хотя видимых действий не было. Это я через react-dev-tools напрямую изменил значение одной позиции. Теперь все работает по-другому. Вы через любой элемент воздействуете на основной модуль, тот отправляет запрос на сервер, получает ответ, накатывает на себя полученные данные и все элементы отрисовываются исходя из актуальных данных. При таком подходе даже если пользователь открыл несколько вкладок и начнет поочередно добавлять-удалять товары в корзине, каждый раз корзина будет актуализироваться при последнем запросе. Ладно, это все теория. Может кто-то хочет сам покликать это и вникнуть поглубже? Сразу скажу, там все довольно сложно, но может кому-то будет интересно. Во всяком случае я попытался все упростить. Как происходит подключение? Уточню, что это сейчас далеко не всем сгодится, пока так, поиграться. 1. Делаем бекап сайта. Да и вообще играемся с этим на тестовом сайте. 2. Качаете и устанавливаете из нашего репозитория https://modxclub.ru/downloads/ компонент frontbasket. 3. Качаете оттуда же modxsite (из него понадобится object-процессор). Напомню, что этот компонент больше ничего не устанавливает кроме себя, предыдущие эксперименты с ним можно забыть. 4. В системных настройках находим и удаляем значение в настройке ms2_frontend_js (так как будет наш самостоятельный обработчик). 5. Создаем себе где-нибудь на сайте js-файл вот с таким содержимым: https://gist.github.com/Fi1osof/00fae120abc921f94a1dc566ad85768c не забываем подключить его в шаблонах сайта. В этот момент при загрузке страницы скрипт уже должен инициироваться на странице. Остается только в нужных частях страниц прописать теги, чтобы скрипт знал куда на странице вклиниваться. 5.1 Миникорзине прописываем id="msMiniCart", именно в этот элемент будет наша миникорзина рендериться (заменит имеющийся элемент). 5.2 На странице просмотра заказа блок товаров должен быть обернут в атрибут data-type="order-details". 5.3 В карточках товаров структура формы и сабмита должна быть такая: 'form.ms2_form [type=submit]' или в представленном файле поправьте эту строчку. Зачем вообще это все нужно?! Ведь так все сложно! Да, все это действительно не легко. Но это все потому что не в вебпаке и шаблонизация в пользовательском файле много места занимает. А так все самое интересное из этого файла укладывается буквально в эти строки: Что здесь происходит? Реактовый компонент смотрит есть ли в объекте window класс FrontBasketTemplates и если есть, инициирует его. Сейчас главная точка входа - метод miniBasket. Там, как вы видите, по цепочке возвращается или карточка пустой корзины, или корзины с товарами. Повторюсь, перерисовка происходит при каждом изменении корзины (если корзина менялась по религии). Изменить корзину можно из любого из этих методов через вызов this.basket.setState(); Кто хочет с этим побольше поиграться, почти ничего не делая, советую в браузер установить react-dev-tools (лучше в хром). Найдите этот компонент в панели реакта Измените какое-нибудь значение и увидите результат Резюме: все это довольно сложно в таком виде. Но если интересно и хотите развития эксперимента, говорите. Может кто-то видит это под другим углом. А если уж совсем интересно и хотите более оформленный компонент получить, заходите в профиль и пополняйте баланс на произвольную сумму. Будет донейтом, который не пропадет просто так.