React – это библиотека для создания компонентов. Он позволяет разбить пользовательский интерфейс на составляющие. Вопрос лишь в том, насколько детализированы эти элементы.
Представьте, что ваша команда только что выпустила приложение ToDo, сделанное на React. Через месяц уже другая команда, работающая в вашей компании, хочет запустить приложение ToDo в рамках своего React-приложения, предназначенного для выставления счетов.
Получается, что приложение ToDo теперь нужно запускать двумя способами:
1. Самостоятельно;
2. В рамках приложения для выставления счетов.
Как лучше поступить в такой ситуации?
Для запуска React-приложения из разных точек есть три варианта:
1. iframe? — внедрение приложения todo в приложение для выставления счетов через
iframe
.2. Многократно используемый компонент приложения? — распространение всего приложения todo через npm.
3. Многократно используемый UI компонент — npm-пакет, включающий только разметку приложения.
Рассмотрим преимущества каждого из этих подходов.
Подход 1: iFrame
Самый простой и очевидный подход — использовать iframe, чтобы поместить приложение ToDo в приложение для выставления счетов. Но тут возникают проблемы:
1. Если два приложения отображают одни и те же данные, есть риск нарушения синхронизации этих данных;
2. Если два приложения используют одни и те же данные, в конечном итоге придется делать лишние обращения к API для получения этих данных;
3. Поведение приложения, помещенного в iframe, нельзя изменить;
4. Когда другая команда выпускает приложение, содержащее ваше приложение внутри iframe, это неминуемо затронет и вас.
Итог: Забудьте! iframe вам не нужен.
Не-не-не!
Подход 2: Многоразовый компонент приложения
Распространение приложения через npm, а не через iframe, позволит избежать проблему № 4 из списка выше, остальные же сохранятся — API, авторизация и поведение. Поэтому я не советую публиковать в npm приложение целиком. Уровень детализации слишком высок, что затрудняет взаимодействие с пользователем.
React-компоненты, которые используются многократно, должны быть детализированными и их должно быть легко компоновать — как детали конструктора LEGO.
Подход 3: Переиспользуемые UI компоненты
Есть более детальный подход, основанный на использовании 2-х составляющих:
1. «Глупые» React-компоненты — только интерфейсы и никаких API-вызовов;
2. API-обертки.
«Глупые» React-компоненты можно легко настраивать, объединять и переиспользовать. При использовании «глупых» компонентов подобным образом вы можете предоставлять необходимые данные или определять, какие API-вызовы должно осуществлять приложение.
Однако, если вы собираетесь объединять несколько «глупых» компонентов, нужно подготовить одинаковый API для нескольких приложений. Вот тут как раз понадобятся API-обертки.
Что такое API-обертки? Это javascript-файлы, содержащие HTTP-запросы к вашему API. Для HTTP-запросов можно использовать, к примеру, библиотеку Axios.
Представьте, что у вас есть пользовательский API. Как сделать пользовательскую API-обертку?
1. Создайте js-файл с публичными функциями, например, getUserById, saveUser
и т.п. При этом каждая функция принимает соответствующие параметры и использует Axios/Fetch для отправки HTTP-запросов к вашему API.
2. Создайте npm-пакета userApi, который будет содержать код вашей обертки.
Пример:
/* Данная API-обертка удобна, поскольку она:
1. Содержит нашу стандартную конфигурацию Axios.
2. Абстрагирует логику для определения baseURL.
3. Предоставляет понятный, простой в использовании набор JavaScript-функций для взаимодействия с API. Это делает вызовы API краткими и единообразными.
*/
import axios from 'axios';
let api = null;
function getInitializedApi() {
if (api) return api; // возвращает api, если он уже инициализирован.
return (api = axios.create({
baseURL: getBaseUrl(),
responseType: 'json',
withCredentials: true
}));
}
// Вспомогательные функции
function getBaseUrl() {
// Поместите сюда логику для получения baseURL посредством:
// 1. Анализа URL для определения окружения, в котором запущено приложение.
// 2. Поиска переменной среды как части процесса сборки.
}
function get(url) {
return getInitializedApi().get(url);
}
function post(url, data) {
return getInitializedApi().post(url, data);
}
// Публичные функции
// Обратите внимание, насколько короткой получилась эта часть благодаря общей конфигурации и вспомогательным функциям выше.
export function getUserById(id) {
return get(`user/${id}`);
}
export function saveUser(user) {
return post(`user/${id}`, {user: user});
}
Распространенной практикой является публикация React-компонентов и API-оберток в npm в виде приватных пакетов, в качестве альтернативы npm может использоваться Artifactory.
Эти «детали LEGO» обеспечивают основу для быстрого создания новых приложений из переиспользуемых элементов.
Система, составные части которой легко компоновать, предоставляет компоненты, которые можно выбирать и соединять в различных сочетаниях для удовлетворения соответствующих требований пользователя.?— ?Википедия
В идеале ваш «глупый» переиспользуемый компонент пользовательского интерфейса должен состоять из других переиспользуемых компонентов, также публикуемых в npm.
С многократно используемыми React-компонентами и API-обертками, опубликованными в npm, легко создать что-то реально крутое — почти как из деталей LEGO.
А какой подход используете вы? Приглашаем обсудить в комментариях к статье.
Комментарии (5)
vintage
19.01.2018 22:41Меня часто спрашивают, как же подобная задача решается в $mol, где все компоненты умные. Думаю вам в Единой Фронтальной Системе тоже будет не безынтересно это узнать.
Итак, берём реализацию ToDoMVC. Всё приложение — это один компонент $mol_app_todomvc: https://github.com/eigenmethod/mol/tree/master/app/todomvc.
Если заглянуть в код, то можно обнаружить, что работа с задачами ведётся через два свойства:
task_ids( next? : number[] ) : number[]
— список идентификаторов задач: https://github.com/eigenmethod/mol/blob/master/app/todomvc/todomvc.view.ts#L20task( id : number , next? : $mol_app_todomvc_task )
— свойства задачи по идентификатору: https://github.com/eigenmethod/mol/blob/master/app/todomvc/todomvc.view.ts#L92
Оба свойства полиморфные, то есть через них можно как читать данные, так и писать.
Давайте воспользуемся этим компонентом в нашем приложении так, чтобы он работал с теми данными, что предоставим ему мы:
Заголовок спойлера<= Todo_widget $mol_app_todomvc task_ids?val <=> task_ids?val / task!id?val <=> task!id?val * title completed false
babylon
20.01.2018 03:13А теперь, добавим нашу логику. Например, загрузим список задач из json файла:
Вы изменили tree!!!???
Я всё жду когда вы перейдете на синтаксис запросов как в E4X https://ru.wikipedia.org/wiki/ECMAScript_%D0%B4%D0%BB%D1%8F_XML. Такие запросы можно перепрограммировать, а не заниматься оборачиваем одной или двух инструкций в функциональную обёртку. Долой функции и классы! Оставляем массивы и группы.vintage
20.01.2018 08:17В js с json все же работать проще. Хотя, конечно, убивает, что нельзя сказать "у вас ошибка на такой-то строке". Е4х, к сожалению, все.
rusmeloman
23.01.2018 19:28Вопрос интересный, но вариант реализации всегда зависит от задачи, которую необходимо решить.
Существуют классические банковские процессы, которые жестко регламентируются законодательством и ВНД. Внутренняя структура таких процессов мало подвержена влиянию бизнес логики самой компании и для всех участников унифицирована. Такие процессы можно создавать целыми кусками и сосредоточить компетенцию и реализацию в «одних руках». Предоставлять готовую сборку для всех участников процесса. Существуют так же базовые сценарии, такие как: аутентификация, идентификация, подтверждение операций и другие. Такие лего-кубики легко подвергаются унификации и их так же можно сосредоточить в одних руках с целью дальнейшего распространения и многократного переиспользования.
В статье приведен пример Задачника (To Do). Если рассматривать банковский процесс с множеством ролей, то это гораздо более сложный механизм презентации информации для пользователя, чем классический задачник, при этом имеет большую зависимость от бизнес логики работы роли. Такой задачник в банковской среде является продуктом бизнес идеи, а следовательно должен проверятся на UX в рамках конкретной роли. Логика работы такого задачника, даже в рамках одной роли, обеспечивается несколькими технологическими сервисами, поэтому невозможно выделить только одну компетенцию для реализации полного решения, что влечет за собой сложное межкомандное взаимодействие и тестирование. Если объединять множество ролей, то потребуется сложная конфигурация решения и возникают ограничения на разработку в инструментальных средствах.
Для Задачника наиболее оптимальным вижу подход разработки «глупых» компонентов и минимальная сборка в npm. При этом имеет смысл выделить команду, которая будет полностью отвечать за технологические сервисы работы с задачами и всей подложки связанной с этим. Команда заказчика будет собирать лего под непосредственным дизайн и архитектурным контролем.
Бизнесу Бизнес!
UbuRus
Блин, все ждал когда капитан очевидность передаст слово и нам расскажут как писать те самые тупые компоненты чтобы они встраивались в любую верстку, имели темы, расширяемость и при этом чтобы их легко было эволюционировать, и т.д. и т.п.