Статья длинная и без картинок. Прежде, чем читать, можно посмотреть пример сервера, на котором 7 моделей 1 уровня с помощью 7 представлений 1 уровня, генерируют 7 приложений, использующих один и тот же скрипт на React. Код на github.
Меня не интересуют приложения, работающие оффлайн, и мне вполне хватает кэша браузера, хотя принцип построения приложения хорошо вписывается в Service Worker .
В этой статье я постараюсь описать принцип разделения кода и данных при создании сложных приложений.
Идея не нова:
От клиента в http-API сервера поступает запрос, ответом на который должна быть html-страница. В моем случае страница должна содержать:
Кроме данных все кэшируется браузером.
Тоже самое поподробней:
newdoc?&home - создать новый документ с формой home.
"rsMode": "edit",
"jsv?api/views/Outline/outline.css::18.07.2018-16:44:17",
"dbAlias": "",
"unid": "",
"printList": "",
"data": {"FORM": "home", "_PAGE_": "1"},
"urlForm": "api.getc?loadForm&home::3133656032",
"multiPage": "",
"userName": "",
"cssJsUrl": [
"jsv?api/forms/home/home.css::04.05.2019-12:32:03",
"jsv?api/forms/home/home.js::18.03.2020-13:17:02"
"jsv?api/views/Outline/outline.css::18.07.2018-16:44:17",
"jsv?api/forms/home/home.css::04.05.2019-12:32:03",
"jsv?api/forms/home/home.js::18.03.2020-13:17:02"]
У меня все приложения открываются с аналогичной страницей. Страницы создаются по шаблону, в который Python подставляет 2 строки: значение тега title и манифест документа window.jsDoc.
Приложение может открыть несколько документов на одной вкладе. В этом случае ему достаточно получить манифест документа в json-формате.
Пример http://react-py.ru/newdoc?&mp_spa
Если клиент захочет сохранить данные в базе, придется вспомнить.
Последовательность обратная: клиент отправляет в json-формате значения полей и имя формы.
АПИ вызывает метод querySave, по имени формы определяя нужный класс.
Метод может сделать дополнительную валидацию, что-то в данных подправить.
Если querySave вернула True, данные передаются модели для записи в БД. При успешном окончании записи клиент получает долгожданный код 200.
В классе формы может быть метод postSave, вызываемый после записи в БД и ответа клиенту. В нем можно отправить данные для индексирования и т.д.
----
MVC хорош тем, что он разделяет зоны ответственности. Модель вернула данные и не вмешивается в действия представления.
Модель может вернуть готовую html разметку. Для такой разметки есть замечательная форма, которая ничего не делает, просто отправляет в контроллер то, что получила.
Если заказчик решил заменить таблицу на график, - нет проблем. Удаляем форму, делаем новую хоть на React, хоть на Plotly-Dash и отправляем в контроллер. Пусть сам разбирается.
В нашем случае форма - это json строка, в которую упакованы контроллеры и элементы разметки. Каждый контроллер имеет имя (имя поля). Он умен (хранит состояние в себе), красив и объектноориентирован.
Есть проект, в проекте около 100 форм и много бизнес логики. Есть около 500 активных пользователей со своими хотелками. Есть задача: сделать все быстро и быстро вносить изменения. Можно каждую форму описывать в реакте и делать обработку акций в редюсере. В результате получим:
Мы пошли другим путем. Каждая открытая на экране форма – это объект класса Document (назовем его doc). После загрузки doc имеет:
Объект doc по url получает от сервера или из кэша форму в виде json строки, парсит ее в React-компоненты и отправляет на рендеринг. При рендеринге в doc создается реестр контроллеров {fieldName: control, …}. Я надеюсь, ref не запретят. Запретить ref, - все равно, что запретить указатели в плюсах.
Контроллер.
Если есть контроллер, есть представление, значит должно быть мидлваре. Контроллеры со списками умеют сами обновлять списки с сервера (метод changeDropList), но во многих случаях без мидлваре не обойтись.
Класс документ умеет обрабатывать с десяток стандартных команд (открыть, закрыть, печать с выбором формы пр.), для других команд служат подгружаемые скрипты.
Для каждой формы свои. Там и находится middleware.
Приложение React изначально ничего не знает о формах, которые ему придется обслуживать и где будут находиться скрипты форм. Все это оно узнает, когда получит url формы.
Скрипты написаны на чистом JS. Чтобы обеспечить связь подгружаемых скриптов формы с объектами класса Document, введена 1 глобальная (ужас) переменная.
Структура скрипта: