Николай Ланец
1 дек. 2017 г., 19:48

Оригинальный интернет-каталог на MODX+node.js+react.js

Всем привет!

Давно я ничего не писал, и вообще меня мало было слышно. Но это не потому что я "завязал", а просто потому что новое хобби оказалось очень объемным и сложным, потребовавшим много времени на изучение и на то, чтобы в итоге можно было действительно что-то вменяемое показать. Хобби - это все то же самое - node.js и т.п. Да, в прошлой статье я писал, что применил эти технологии здесь, но все равно здесь это совсем еще не то. Здесь это еще совсем начальные эксперименты были, а вот на другом своем проекте я боле менее серьезные вещи уже реализовал. Другой проект - это Городские-бани.ру, про которые я ранее уже писал, и которые сейчас пережили очередную реинкарнацию. Бани тоже еще не совсем законченные, там много чего еще сверху навернуть надо, но в целом основа заложена очень интересная, уже есть чем поделиться. К сожалению, я в этой статье не приведу подробные рецепты работы и готовые решения, но если кто любит почитать что-нибудь просто для расширения кругозора, вполне статья может оказаться интересной.

Для начала кратко опишу стек технологий:
- Изначально бэкэнд полностью на MODX был, сейчас MODX - это хранение основной части информации + политики безопасности.
- Фронт - react+material-ui+graphql+google-map-react+draft-js и т.п., в общем сплошь JavaScript.
- Между всем этим node-js+react-dom-server, обеспечивающий запросы на MODX и серверный рендеринг.

Уверен, для многих это все покажется очень сложно и громоздко, но уверяю, все не так на самом деле. На самом деле все еще гораздо сложнее))). Можно сказать, что я слишком увлекся несколькими отдельными, занятными на первый взгляд, но не всегда совместимыми друг с другом, технологиями, и с учетом того, что это проект мой и за все эксперименты я несу ответственность только перед собой, я позволил себе гораздо больше, чем, может, имел права. В итоге я несколько раз переписывал проект в различных вариациях, и на сегодня получил боле менее взвешенный результат, который и поисковиков не сильно расстраивает, и мобильные устройства пользователей не делает плакать, да и вообще вроде нормально работает.

Перейду от сложного сухого текста к описанию проекта со скриншотикам, чтобы было понятней о чем вообще речь...

Вообще изначально я хотел сделать картографический сервис, где главной страницей была бы карта. Сейчас карта несколько на второй план ушла, но все равно является большой частью проекта. Вот прямая ссылка на карту, если вдруг кто не заметит сразу кнопку: https://gorodskie-bani.ru/moscow/@55.7485,37.6184,12

Почему карта ушла на второй план? Вот тут я, что называется, получил удар оттуда, откуда не ожидал... Засада оказалась в том, что на сегодня доля мобильного трафика сайта составляет почти 70%. У меня не топовый телефон, но и не самый слабый, и даже с него, заходя на сайт с картой, объективно, я мог начать взаимодействовать с сайтом только секунд через 20-30. Все потому что почти мегабайт javascript + загрузка гугло-карты с прочими сторонними ресурсами - это довольно много для того, чтобы телефон мог это все быстро пережевать. С компьютерами такой проблемы нет, но если бы доля мобильного была не более 10%, то я мог бы еще закрыть глаза на это, а так не могу. В итоге пришлось переосмысливать проект и переписывать его. А переписать пришлось очень многое... Дело в том, что изначальная реализация проекта была довольно оригинальная: связка react+graphql+flux+immutable позволила мне организовать такую систему, что на сторону браузера загружались все сырые данные разом, после чего сайт работал как полностью автономное веб-приложение, которому интернет уже почти не был нужен (только для подгрузки картинок). По нему можно было часами лазить без единого запроса на сервер, и работало это очень быстро. И вот от этого пришлось отказаться, но плюс к этому переписать и организовать подгрузку нужных данных так, чтобы это не ударило по функционалу, и не было много лишних запросов на сервер. И в итоге переписал.

Итак, какие же фишки на выходе получились, что я так много буков написал уже?

1. Довольно интересная подача контента в зависимости от того, куда пользователь зашел при первом посещении. Алгоритм такой:
- Сначала из адресной строки берутся гео-данные, если они указаны, то есть как в ссылке выше @55.7485,37.6184,12. Если пользователь заходит на страницу с такой меткой, то не важно откуда он, "гео-центр" сайта фиксируется в этом положении и все карточки заведений выстраиваются относительно этого центра, то есть в первую очередь будут выводиться ближайшие, и в последнюю очередь наиболее отдаленные.
- Если гео-меток нет в адресной строке, то берутся данные из самой страницы. Это может быть страница города (если пользователь зашел на страницу с указанием города), и тогда центр берется от этого города, или данные организации, если он попал на нее. Таким образом даже в рамках одного города, в зависимости от того, в какую часть сайта пользователь попал. ему будет выдан тот или иной контент. Это сделано из соображений того, что как правило при поиске бань пользователю очень критичен географический аспект, и если те бани, что он искал, в данный момент закрыты или типа того, если если и заинтересуют другие заведения, то скорее всего только поблизости. Все остальное для него не будет иметь значения.
- Если гео-данные не удалось определить на основании контента, то тогда уже берутся данные самого пользователя на основе geoip.

В дальнейшем планируется еще расширить логику определения геоданных содержимого. В частности, уже сейчас можно, к примеру, из статей-обзоров заведений, привязанных к заведениям, и их комментариев, определять гео-принадлежность контента. На мой взгляд, такой подход очень положительно может сказаться на гео-подаче сайта в рамках страны и за ее пределами, так как поисковики довольно хорошо в наше время определяют откуда пользователь и какой контент в индексе соответствует его региону. И когда пользователь попадает на страницу с географической принадлежностью, перейдя с этой страницы на главную страницу сайта, он получит контент, более "близкий" к нему, а не все подряд от корня с предложением "Пожалуйста, укажите свой город". Даже города в главном меню выстраиваются в порядке удаленности от текущего определенного города, и если вы начнете двигать карту в сторону других городов, увидите, что список городов в главном меню меняется.

Если кому не лень, зайдите на главную страницу https://gorodskie-bani.ru/ и потом отпишитесь в комментариях, все ли так, определилось ли ваше местоположение и были ли в первую очередь показаны заведения, наиболее близкие к вашему местоположению?

2. В этом же ключе в самих карточках заведений есть дополнительный блок "Другие заведения поблизости". Так же берутся ближайшие к текущему заведения.

3. Довольно оригинально реализован редактор графика работ. В банях есть, кроме общего графика, мужские дни, женские, семейные. Вот так это работает:

4. Отдельная история - редактировние объектов на сайта. После долгих размышлений, я пришел к выводу, что любой должен иметь возможность редактировать информацию в заведениях, даже не будучи авторизованным. Почему я так решил? Да ведь полно примеров в том же 2gis и яндекс-справочниках, когда люди в комментариях пишут типа "А здесь цены уже не такие, а такие", или "График поменялся, теперь он такой". При чем эти комментарии не редко пяти-шестилетней давности, и их много и они противоречат друг другу. И не ясно что сейчас актуально. Я считаю, что если человек знает что-то и хочет поделиться этим, то ничто не должно ему мешать это сделать, и даже не надо требовать авторизацию. На банях это именно так и реализовано. И тут, конечно же, каждый спросит "а как же безопасность и т.п.?". Отвечу: с этим все ОК. Алгоритм следующий:
- Если права у пользователя есть на редактирование объекта, то пусть сохраняет (это может быть как администратор, так и владелец заведения).
- Если это авторизованный пользователь, но у него нет прав на этот объект, или это анонимный пользователь, то им сообщается в духе "Ваша заявка принята, после проверки модератором данные появятся на сайте" и измененные данные сохраняются в таблицу версий. Есть даже отдельная страница для вывода статистики изменений https://gorodskie-bani.ru/edits
Я, будучи суперпользователем, вижу в том числе и какие данные были переданы. Данные в техническом виде, но мне их достаточно.

Вот как происходит модерация и принятие изменений:

5. Оригинальный редактор текста. Это такой же редактор как и здесь, то есть на базе draft-js, но значительно доработанный. В частности, можно вставлять как отдельные картинки, так и целые галереи. Но главное - это можно вставлять ссылки на другие заведения, и выводиться потом будет не просто ссылка, а карточка заведения, выводимая по клику.

Этот же редактор и в комментариях можно использовать, то есть тоже ссылки на объекты вставлять. В дальнейшем можно будет в карточке компании выводить все упоминания в статьях и комментариях.

Но главная фишка - это, конечно же, серверный рендеринг. При чем не просто так, а практически зеркальный тому, что происходит на клиенте. И здесь не все так однозначно. Довольно много статей написано, касательно SSR, но далеко не во всех статьях уделяется внимание тому моменту, что работа реакта на клиенте и на сервере, по большому счету, сильно отличается. По больше степени это касается того, что на клиенте реакт работает циклически, обрабатывая все изменения состояния и входящих параметров. Пример: Для полного рендеринга данных в компоненте, вам надо запросить данные с удаленного сервера Ajax-запросом, получить их, закинуть в стейт компонента, после чего он выполнит ре-рендеринг с этими данными. Ajax-запрос выполняется асинхронно, а в реакте нельзя заблокировать работу компонента, так, чтобы он отрендерился только после получения данных. В реакте можно только отработать компонент, потом заставить его повторно отрендериться, при чем делать это сколько угодно раз. А вот на сервере с реакт-компонентом этого сделать нельзя. Там повторного рендеринга нет, все необходимые данные должны быть переданы еще на входе. Честно сказать, это задача, которую я до конца не решил. Дело в том, что на фронте, по мере получения данных и ререндеринга компонентов, могут в вывод приходить новые компоненты, которые тоже будут запрашивать еще другие компоненты и т.д. То есть всей цепочки предугадать очень сложно. В итоге пришлось сайт разбить на какие-то отдельные, боле менее понятные блоки, в которых легче прогнозировать весь необходимый для рендеринга набор данных и работать с ними.

Послесловие: поработав уже довольно продолжительное время со всеми этими технологиями, хочу отметить, что технический уровень современных проектов по-прежнему растет. Я давно работаю с MODX (и не только с ним работал), и есть с чем сравнивать. Могу точно сказать, что на чистом MODX такое было бы мне очень сложно сделать. И тут следует оговориться: дело не столько в самом MODX. Дело в том, что в случае с MODX (реализации проекта на нем и используя его для шаблонизации), мы делаем параллельно как бы два проекта: один на сервере, для всей серверной логики, а второй на фронте (javascript и т.п.). В этом случае клиентская логика и серверная как бы отделены друг от друга. Они как бы живут каждый своей жизнью. Ввести в таких условиях какую-то хитрую динамику на стороне браузера - сложное занятие, потому делается это в рамках, как правило, только необходимого. В случае же с react, граница между клиентом и сервером размывается. И это очень интересно.
Круто. У нас вот из-за проблемы получения на старте всего стейта приложения в итоге отказались от сервер сайд рендеринга. Или он просто не работал. Я в детали сильно не углублялся - у меня было полно других задачь. Но тимлид в итоге назвал ошибкой выбор reflux вместо redux. Redux как раз эту проблему сервер сайд рендеринга бы решил.
На самом деле не думаю, что reflux, redux или flux решили бы эту проблему. Да, на них можно отправить изменения, которые повлияли бы на запрос данных и в итоге получили бы измененный стейт приложения, но повторюсь, новые компоненты могут порождать другие компоненты и т.п. Это бы добавило коллизий. Этих коллизий можно было бы избежать только в случае, когда все изменения проходят через подобные хранилища без самостоятельной логики в самих компонентах. То есть компоненты только для отрисовки. Но на своем опыте я осознал, что подобные решения слишком неповоротливые получаются. Да, предсказуемость растет, но гибкость падает и объем кода растет. Я для себя решил это так: при заходе на страницу (первый запрос на сервер), система через react-router получает запрашиваемый компонент (тот, который на основе настроек роутера отрабатывался бы на стороне браузера, на основании адресной строки). У этого компонента запрашивается метод получения данных. Это GraphQL-запрос. Именно GraphQL в данном случае спасает, потому что через него можно одним запросом получить сразу несколько дата-сущностей с различной вложенностью. Полученный результат возвращается в обработчик запроса, который тогда только вызывает непосредственно реакт-компонент через react-dom-server с непосредственной отрисовкой HTML. Тут он уже получает не только HTML, но и конечный снимок глобального стейта (то есть в этот момент в самом рекат-компоненте может выполниться дополнительная логика на основании переданного стейта, которая не требует ожидания асинхронных запросов и ре-рендеринга). Этот уже HTML передается вместе со снимком стейта в брайзер и в этот момент, даже после выполнения реакт-компонента на стороне браузера, мы получаем точную копию того, что произошло на сервере. А далее уже в браузере работает полноценное веб-приложение со всеми асинхронными запросами, отсутствием перезагрузки страницы и т.п. Попробуйте отключить JS и зайти на сайт бань, 95% сайта будет работать без JS, вы почти не заметите разницы, все ссылки рабочие, и даже если вы будете авторизованы, с сервера будет приходить код с вашей аватаркой, учетом уровней доступов и т.п.
Николай приветствую! Очень обрадовала твоя статья! В целом динамика твоих изысканий все круче и все выше! Познавательно и реально интересно! Такой вот вопрос, я взялся изучать Angular 4, как ты смотришь на возможности этого фреймворка, в том числе в свете твоих работ? Может смысл есть его функционал задействовать?
Сергей, добрый день.
С ангулар в бою так и не столкнулся, и нет особого желания. Это готовый фреймворк, а мне больше нравится низкоуровневость. К тому же помимо реакта, фейсбук предлагает целый спектр попутных технологий: react, draft-js, graphql, flux, immutable, react-native, а теперь еще и react-vr. Это очень широкий и объемный спектр технологий. На мой взгляд это направление перспективно, не вижу сейчас смысла дергаться еще куда-то.
Обана... Не уж то существует в интернете место, где есть что-то интересное на счет совместной работы nodeJS и MODX... У меня сейчас мало времени, чтобы успеть нормально пробежаться по всем статьям, по поводу них, но 100% уже завтра вечером сяду и все прочитаю. Сам сейчас просто в неописуемом восторге от JavaScript. Все началось с ознакомления с Google'овским Progressive WebApp (https://developers.google.com/web/progressive-web-apps/ ), а в частности ServiceWorker и его возможностями для работы сайтов из оффлайна + бешеная скорость загрузки/работы за счет асинхронности у всех элементов сайта... И с тех пор пошло-поехало... Но все это время меня удивляла одна простая вещь: если взять тот же самый Gulp, то на самом деле сборка шаблонов на нем не такая уж и сложная задача... Но при этом на том же modx.pro ни слова на эту тему... А тут на тебе, аж целый React :-) Если где-то еще будет информация про Polyfill, то жизнь уж совсем станет сказкой :-)

P.S. ReactVR - это реализация виртуальной реальности... И это не просто "очень широкий и объемный спектр технологий" , но так же и ОЧЕНЬ не слабых знаний геометрии, как всей остальной математики.
Реакта здесь будет много. Я сейчас заканчиваю перенос довольно крупного тематического портала на свой движок (готовых комплексных решений под себя так и не нашел, поэтому пилю свое сам. С учетом того, что на базе того, на чем работают городские бани, у меня еще один крупные проект создается, и за эти выходные, за два дня, я почти полностью переписал 10-ти летный проект, который крутился на самописке, на эту новую платформу, думаю, могу уже называть это движком). В начале года будет очередное масштабное обновление сайта Клуба на этот движок, так что все это будет неотъемлемой частью MODX-Клуба, и будет довольно подробно освещаться.
Я не рассматриваю MODX и Javascript как конкурирующие взаимозаменяемые технологии. Они вполне могут существовать совместно. MODX более проще и более предсказуемый, и вполне годится для бэкэнда. А Javascript можно использовать для обновления проекта, создания более современного фронта. Собственно, так же как ангулар и все остальное используется в связке с ларавелем. Далеко же не все, кто использует их в связке, говорит "Так, от ларки отказываемся, теперь только кашерный javascript!".

Про полифил читал, но пока не вижу для себя необходимости его использовать. Мне достаточно того, что реакт позволяет очень быстро менять свойства выводимого компонента, и нагромождать технологии - это скорее всего лишнее будет.

VR всерьез заинтересовался после вот это статьи:

Немного поигрался как пользователь, показалось очень занятным направлением. Математики и геометрики за нас уже все сделали.

К слову, после того. как это чуть изучил, открыл для себя, что в ютубе очень много 3D-контента. В тот момент немного приуныл, ощутил, как будущее проходит мимо меня. То есть сидишь, делаешь такой что знаешь, а тут уже вокруг много того появляется, чего ты просто не заметил.

P.S.
>> бешеная скорость загрузки/работы за счет асинхронности у всех элементов сайта...

Бани - довольно тяжеловесный проект получился. Там полно мест еще, где надо оптимизировать. Но даже при этом гугл-спид показывает для компьютеров 97/100

Для мобильников результат похуже, но тоже не плохой, 75-85.
По поводу рейтинга бань могу дать пару советов:
1) Gulp - для сборки фронта - это наше все. Глянь модули gulp-critical-css (Крит. CSS думаю знаешь, что такое) + gulp-uncss ( анализирует HTML код и находит все неиспользуемые и продублированные стили).
2) На счет кэша браузера, если не путаю, то вроде как, если в редакторе счетчика метрика на Яндексе выбрать "Альтернативный CDN", то проблема уходит.
3) На счет картинок не думаю, что смогу что-то новое для тебя сказать... Мне почему-то кажется, что скорее всего ты тут банально схалтурил :-)

P.S. Когда узнал про gulp-uncss и то, что он "анализирует HTML код и находит все неиспользуемые и продублированные стили и удаляет их", то ,в отличии от тебя, не просто " В тот момент немного приуныл"... Я конкретно о&$%ел. Я несколько лет назад понял, что наступает новая глава в истории программирования, когда сидел за очень слабым офисным компьютером и играл в Quake 3 прямо в браузере... Ну а далее зайдя в Google Experiments я окончательно сформулировал для себя, что пришла эра JavaScript...

UPD: Глянул сайт и еще добавлю gulp-autoprefixer... т.к. нет "-webkit-" , "-moz-" и т.п. для поддержки старых браузеров... Работал как-то раньше с банькой одной и могу сказать со 100% уверенностью: публика там и на 8 Internet Explorer может сидеть :-)))))
Картинок там только две, на которых он ругается - аватарки 150 на 150, там, где они реально меньше нужны. На счет "схалтурил", ты вообще погорячился. С картинками как раз у меня там все вполне нормально. В модели прописаны сразу несколько вариаций картинок.
"image": "assets/images/lazy/3db3624b49a80589c6704af6759ecdff.jpeg", "imageFormats": { "thumb": "images/resized/thumb/assets/images/lazy/3db3624b49a80589c6704af6759ecdff.jpeg", "small": "images/resized/small/assets/images/lazy/3db3624b49a80589c6704af6759ecdff.jpeg", "middle": "images/resized/middle/assets/images/lazy/3db3624b49a80589c6704af6759ecdff.jpeg", "big": "images/resized/big/assets/images/lazy/3db3624b49a80589c6704af6759ecdff.jpeg" },
Для топиков еще больше вариаций.

Основное, на что ругается гугл, так это на объем HTML-кода "Для показа верхней части страницы понадобилось 156,8 КБ данных с сервера" и долгий ответ сервера - +-1 секунда. Но я не планирую в угоду этого архитектуру менять. С мобильника я пробовал, уже через 2 секунды я могу взаимодействовать со страницей, до того, как 15% контента подгрузилось (имеется ввиду со скриптами и т.п.), я считаю, что это вполне нормально. Я без 100/100 вполне переживу :)

С советами этими чуть опоздал.
1. Галп, ИМХО - прошлый век. От него аж корежит. Отладка в нем - вообще Адовый АДъ. Пока webpack. Позже может на фейсбуковый сборщик пересяду, если станет стандартом.
2. Стили - вообще не актуально. Глянь CSS. Там fontawesome и бутстрап. От одного и второго откажусь скоро, они просто прилетели наследием, просто потому что мне было лень переделывать шапку. А основные стили в теле документа ищи. Ты их не зачистишь сторонними решениями, там все уникальное. Фишка в том, что это динамические стили. Раз правило в документе есть, значит есть какой-то элемент, для которого это правило нужно. jscss слышал? material-ui@alpha/beta
>>> На счет "схалтурил", ты вообще погорячился
Это была шутка :-) Я прекрасно понимаю, что было что-то из серии:
>>> аватарки 150 на 150, там, где они реально меньше нужны.

>> Основное, на что ругается гугл, так это на объем HTML-кода "Для показа верхней части страницы понадобилось 156,8 КБ данных с сервера" и долгий ответ сервера - +-1 секунда.
Это-то понятно... В будущем Google видит Progressive WebApp, а у них как раз по ним и приколы главные. Вот, к примеру результаты прогона сайта бань в LightHouse:


Но тут уже из другой оперы, конечно, тестирование. Особенно меня радует кэширование на стороне клиента и за счет этого работа даже без интернета:

Если мало-ли вдруг понадобится, то вот чеклист: https://developers.google.com/web/progressive-web-apps/checklist

Да и на счет...
>>> Раз правило в документе есть, значит есть какой-то элемент, для которого это правило нужно. jscss слышал? material-ui@alpha/beta
Про jscss нет, не слышал... Я особо в React не лазал... Пока что в сторону Polymer буду курс держать... Там уж точно:
>>> Раз ... в документе есть, значит есть какой-то элемент, для которого это ... нужно
:-)
ОК, возьму на заметку, но сейчас для меня это пока рановато. Я развиваю самую логику, и только когда она будет окончена, можно будет заниматься оптимизацией. Сейчас в таком случае это будет Разработка-Оптимизация-Тестирование-Разработка и так далее в цикле. Лишнее звено, при чем весьма ресурсоемкое.
Вот еще один проект запустил на этом движке: http://pivkarta.ru/
Этот проект уже гораздо сложнее и объемней, чем городские бани. Яндекс в результаты выдает 83 000 страниц.
Сущностей на сайте:
- 1 500 видов пива.
- 555 + 175 записей в блоги и событий.
- 3 300+ комментариев.
- 3 500+ заведений.
- 8 300+ фотографий.
- 26 500 пользователей (не вычищал фейков, пока не знаю долю реальных пользователей, но их большая часть).

В общем, сайт предъявил серьезные требования к разработке и оптимизации. Когда запустил первичную версию, нода на запуске съедала больше 2 Гб оперативки (затем в стабильном режиме 700+ метров), получение всех API-данных занимало более 40 сек, и страницу отдавало за 1-10 сек.
Сейчас штатный режим менее 500 метров требует (на запуске пик чуть более гига), страницу отдает 0.4-1 сек (на паре страниц до 1,5 сек, но это поправлю).
Прямо в тему оповещение пришло про комментарий. Сейчас изучаю сайт один, который после 2-3 месяцев разработки ... Мне вот интересно , а степень моего веселья понять можно всего лишь по скрину файлового менеджера из админки сайта? 34к взял чел за натяжку на MODX.

P.S. почему-то правда картинка через место одно выводится в теле комментария, поэтому надо смотреть по ссылке.

P.P.S. !!!! 2-3 МЕСЯЦЕВ НЕ МОЕЙ РАЗРАБОТКИ !!!!!

Чтот я не уловил посыла. Можно чуть разжевать?
Посыл в том, что хочу узнать твое мнение, т.к. не могу понять: JS кретинизм- это проблема только среди MODX разрабов или сейчас вообще везде острая нехватка Frontend разрабов? В последнее время все больше поражает, какие все крутые/важные на modx.pro , хотя кроме пыхи и AjaxForm/AjaxSnippet от Васи не знают ничего. Трындец какой-то.
Я все равно не уловил в чем JS-кретинизм. Ты про то, что кто-то накидал все в корень сайта? Ну да, это фигня полная. Но это не только касаемо JS косяк. Кидать в корень что угодно - глупость.

Добавить комментарий