На старте проекта обычно встает вопрос о выборе готовой ui-библиотеки для решения шаблонных задач, таких как создание форм, инпутов, кнопок и других компонентов. Количество готовых ui-библиотек для React так стремительно растет, что уже сложно остановить свой выбор на какой либо из них. Зато в таком разнообразии каждый может найти библиотеку, подходящую под его задачи. В этой статье хочется рассказать о фреймворке Steroids, который разработан и поддерживается в нашей компании.
Изначально мы не планировали создавать фреймворк, а просто собирали удачные решения рутинных задач. Получился набор полезных утилит и мини-библиотек, который позволял нам работать быстрее. Мы постепенно добавляли в него новые элементы, он рос и видоизменялся, и в итоге вырос в полноценный фреймворк Steroids.
Что же такое Steroids и для каких задач он может подойти?
Steroids — это фреймворк, который задает архитектуру для всего проекта, а также содержит набор как простых, так и комплексных ui-компонентов, инструменты для взаимодействия с бэкендом и работой с формами, компоненты для работы с хранилищем браузера, websocket и другими технологиями.
Фреймворк Steroids подходит для создания полноценного SPA приложения с продуманной архитектурой, которое содержит в себе:
-
роутинг и управление состоянием, в том числе с использованием Redux, React Context;
-
взаимодействие с сервером — отправка запросов, обработка ошибок;
-
работу с формами — валидация, отправка данных на сервер, сохранение данных полей формы в state manager;
-
загрузку и отображение данных в виде списка или таблицы, сортировка, пагинация, фильтрация данных;
-
локализацию всего приложения;
-
использование серверного рендеринга приложения (SSR).
Основные фичи и особенности фреймворка
У фреймворка Steroids много особенностей и фич, о которых стоит рассказать. Всё это точно не влезет в одну статью. Поэтому здесь пойдет речь про некоторые из них:
-
фреймворк задаёт определенную структуру проекта;
-
все ui-компоненты разделены на
coreиviewчасти; -
во фреймворк входят не только ui-компоненты, но и логические компоненты, которые «под капотом» используют другие инструменты или библиотеки.
Структура проекта
Фреймворк Steroids задаёт определенную структуру папок и файлов проекта. Эта структура предполагает отдельные директории для переиспользуемых компонентов, редьюсеров и экшенов, роутов приложения, стилей, модальных окон и так далее. Ниже приведен пример такой структуры:
├── node_modules ├── public ├── src │ ├── actions │ ├── reducers │ ├── modals │ ├── routes │ │ └── IndexPage │ │ └── views │ │ ├── MyBox.ts │ │ └── MyBox.scss │ │ ├── IndexPage.ts │ │ ├── IndexPage.scss │ │ └── index.ts │ ├── shared │ │ └── Layout │ │ ├── Layout.ts │ │ ├── Layout.scss │ │ └── index.ts │ ├── ui │ │ └── form │ │ ├── Button │ │ │ ├── ButtonView.tsx │ │ │ └── ButtonView.scss │ │ └── CustomField │ │ ├── CustomField.ts │ │ ├── CustomFieldView.tsx │ │ └── CustomFieldView.scss │ ├── style │ │ ├── index.scss │ │ └── variables.scss │ ├── Application.tsx │ └── index.tsx ├── .eslintrc ├── index.d.ts ├── package.json ├── tsconfig.json ├── webpack.js └── yarn.lock
Созданное на основе Boilerplate приложение имеет нужную структуру, в нём созданы все необходимые папки и файлы. Оно представляет собой тривиальное SPA-приложение с единственной страницей, и сразу после развертывания готово к запуску. Это приложение можно наполнить своими страницами и компонентами, располагая их в соответствующих структуре папках.
Разделение логики и отображения компонентов
Чтобы логика работы компонента была отделена от его отображения, каждый ui-компонент разделён на core и view часть.
Core — ядро компонента. Оно содержит всю бизнес-логику компонента, принимает и обрабатывает полученные данные из props, находит или получает нужную «вьюшку» (view-часть компонента) и прокидывает в неё подготовленные данные. Core-компонент практические не содержит jsx кода и обращений напрямую к DOM.
View — отображение компонента. Это часть, которая отвечает за непосредственную отрисовку компонента. Как правило она содержит только jsx код и scss стили компонента, без бизнес-логики. При кастомизации компонента его View.tsx и/или View.scss файлы при необходимости могут полностью копироваться из фреймворка Steroids и изменяться под дизайн конкретного проекта.
Если при разработке достаточно стандартного отображения компонента, то его можно просто импортировать из core-части фреймворка Steroids (npm-пакет @steroids/core) и использовать в нужном месте. Если же есть потребность как-то кастомизировать компонент под дизайн проекта, то можно воспользоваться несколькими способами, один из которых работает как раз благодаря разделению компонента на core- и view-части.
Вот три основных способа кастомизации:
-
Передача через пропсы css-классов или объекта
style. -
Переопределение css-переменных.
-
Переопределение view-части компонента — его стилей или tsx-кода.
Как использовать первые два способа, думаю, понятно. Они есть в большинстве готовых ui-библиотек. Поэтому перейдем к примерам того, как можно переопределить view часть.
Первый и самый простой способ — передать кастомное отображение через проп view:
const CustomInputFieldView = (props: IInputFieldViewProps) => ( <div> <span>Кастомное отображение инпута</span> <input {...props.inputProps} placeholder={props.placeholder} disabled={props.disabled} required={props.required} /> </div> ); const MyForm = () => ( <Form> {/* инпут со стандартным отображением */} <InputField attribute='name' /> {/* инпут с кастомным отображением */} <InputField attribute='surname' view={CustomInputFieldView} /> </Form> );
Этот способ подойдет, если нужно переопределить какой то конкретный компонент, например инпут для поиска в шапке сайта. Если же нужно добавить кастомное отображение для всех инпутов в проекте, то лучше воспользоваться другим способом.
Второй способ заключается в полной замене стандартных view-частей компонентов на кастомные. В этом случае «вьюшки» применяются ко всем компонента сразу, например ко всем Button или ко всем Form.
Файлы со стилями компонентов подключаются в src/style/index.scss через конструкцию @use, например:
@use '~@steroidsjs/bootstrap/form/InputField/InputFieldView';
А файлы с tsx-кодом компонента в src/ui/bootstrap.ts, например:
'form.InputFieldView': { lazy: () => require('@steroidsjs/bootstrap/form/InputField/InputFieldView').default },
Чтобы переопределить view-часть, достаточно изменить пути к файлам — к scss-файлу или tsx-файлу, смотря что нужно кастомизировать.
Логические компоненты
Одна из особенностей Steroids состоит в том, что «из коробки» доступны не только ui-компоненты, но и логические компоненты или, по другому, компоненты-обертки над другими инструментами и библиотеками. Каждый компонент представляет из себя класс с набором свойств и методов.
Инициализируются эти компоненты в проекте в файле src/Application.tsx в константе config в поле components:
export const config = { reducers: require('./reducers').default, routes: () => require('routes').default, layoutView: () => require('shared/Layout').default, screen: {}, theme: {}, components: { locale: LocaleComponent, http: HttpComponent, resource: ResourceComponent, }, }
В этом же конфиге можно задать свойства для компонентов, например:
components: { http: { className: HttpComponent, apiUrl: 'https://steroids.dev/', }, },
Используются такие компоненты внутри обычных react компонентов, а получить их можно из хука useComponents. Пример:
export default function Header() { const bem = useBem('Header'); const {http} = useComponents(); const [user, setUser] = React.useState(null); const getUser = () => http.get('api/v1/user').then( response => setUser(response.data) ); return (<>...</>) }
В Steroids реализовано более десяти таких компонентов. Вот основные из них:
HttpComponent — используется для отправки запросов на бекенд, обработки ошибок, авторизации. Поддерживает авторизацию через токен. «Под капотом» использует библиотеку axios.
JwtHttpComponent — расширяет HttpComponent, добавляя функционал обновления токена авторизации.
LocaleComponent — локализует приложение, поддерживает конфигурацию языка и временной зоны.
ClientStorageComponent — работает с хранением данных в браузере (cookie, local/session storage).
WebSocketComponent — создаёт websocket-соединения и управляет ими
UI-Kit, простые и комплексные компоненты
В Steroids ui-компоненты условно делятся на простые и комплексные.
Простые компоненты — компоненты интерфейса, которые можно использовать независимо от других компонентов, например кнопка, форма, список.
Комплексные компоненты — состоят из простых компонентов, логически связанных между собой, и реализуют какой либо сложный функционал. Примеры таких компонентов — канбан, таблица, чат и др.
Все компоненты в UI-Kit фреймворка Steroids разделены на несколько групп:
-
content;
-
form;
-
layout;
-
list;
-
nav;
-
typography.
В каждой группе собраны компоненты, отвечающие за определенный функционал — отображение контента, формы и списки, навигацию и так далее.
Вот примеры простых ui-компонентов из Steroids, которые наиболее часто используются в проектах:
Accordion — компонент-аккордеон позволяет создавать интерактивные списки или контейнеры, где содержимое может быть развернуто или свернуто по требованию пользователя.
DropDown — компонент, представляющий меню с элементами, которые могут быть выбраны или нажаты. Компонент позволяет отображать и скрывать содержимое меню, а также управлять его позиционированием.
Button — кнопка или ссылка. Используется в интерфейсе для выполнения какого-либо действия по клику.
InputField — поле ввода текста.
NumberField — числовое поле ввода.
FieldList — список из сгруппированных полей формы.
CheckboxField — одиночный чекбокс, использующийся в формах для отметки булевого значения.
Form — компонент для создания формы. Предоставляет управление и синхронизацию состояния формы, а также позволяет выполнять отправку данных формы на сервер с возможностью валидации и обработки результатов.
Notifications — компонент, представляющий собой контейнер для отображения всплывающих уведомлений.
Tooltip — компонент, предоставляющий всплывающую подсказку для дочерних элементов.
Grid — представление данных коллекции в виде таблицы.
List — компонент для представления коллекции в виде списка.
Nav — компонент навигации позволяет переключаться между группами связанного контента.
Комплексные компоненты используются реже, так как реализуют функционал, заточенный под узкий круг сложных задач.
Вот примеры некоторых компонентов:
CalendarSystem — полноценная система календаря, которая поддерживает создание событий и добавление их групп, разделение событий по пользователям, отображение сетки событий в трёх вариациях — день, неделя и месяц.
Kanban — компонент для работы со списком карточек, сгруппированных в списки. Поддерживает функционал создания и редактирования карточек, добавления к ним тегов и описания, открытия карточек в детальном виде — в модальном окне.
Chat — реализует функционал чата, поддерживает загрузку данных при отправке сообщений, просмотр истории сообщений, разделение сообщений по датам, прикрепление файлов к сообщениям.
Остальные простые и комплексные компоненты UI-Kit Steroids можно посмотреть по ссылке — https://steroids.dev/ru/docs/ui.
Сравнение с другими библиотеками
Мы решили сравнить функционал разных ui-библиотек для React, которые пользуются популярностью. Для сравнения выбрали несколько пунктов, которые считаем важными при выборе библиотеки:
-
наличие UI-Kit;
-
готовая архитектура для проекта;
-
подключенные «под капотом» базовые зависимости для создания SPA приложения;
-
работа с формами;
-
работа со списками (в том числе пагинация и фильтрация);
-
работа с роутингом;
-
работа с SSR;
-
возможность кастомизировать компоненты;
-
наличие активного сообщества.
Для сравнения мы выбрали несколько библиотек:
-
Material UI;
-
Ant Design;
-
Mantine;
-
Semantic UI React;
-
Steroids.
Вот что у нас получилось:
У всех выбранных библиотек есть Ui-Kit и возможность разными способами кастомизировать компоненты. А вот с остальными критериями не все так гладко.
Готовая архитектура проекта, настроенный роутинг и подключенные «под капотом» зависимости есть только у библиотек Material UI, Mantine и Steroids. При чем у Material UI такие функции доступны только в платных шаблонах.
Работа с SSR есть только у двух библиотек — Mantine и Steroids. У первой «под капотом» используется Next.js, а у второй написана собственная реализация SSR, которая подключается через npm-пакет @steroids/ssr.
Посмотрим как обстоят дела с компонентами. В каждой из приведенных библиотек есть компоненты для полей формы, но только в двух из них (все те же Mantine и Steroids) «под капотом» реализована логика работы с формами, а именно валидация значений, обработчик onSubmit, режимы контролируемой и неконтролируемой формы.
Вот пример использования форм, взятый из документации библиотеки Mantine:
import { Button, Checkbox, Group, TextInput } from '@mantine/core'; import { useForm } from '@mantine/form'; function Demo() { const form = useForm({ mode: 'uncontrolled', initialValues: { email: '', termsOfService: false, }, validate: { email: (value) => (/^\S+@\S+$/.test(value) ? null : 'Invalid email'), }, }); return ( <form onSubmit={form.onSubmit((values) => console.log(values))}> <TextInput withAsterisk label="Email" placeholder="your@email.com" key={form.key('email')} {...form.getInputProps('email')} /> <Checkbox mt="md" label="I agree to sell my privacy" key={form.key('termsOfService')} {...form.getInputProps('termsOfService', { type: 'checkbox' })} /> <Group justify="flex-end" mt="md"> <Button type="submit">Submit</Button> </Group> </form> ); }
И вот пример, как формы используются в Steroids:
import {login} from '@steroidsjs/core/actions/auth'; import {ROUTE_USERS} from 'routes'; const FORM_ID = 'LoginFrom'; export default function LoginFrom() { return ( <div className={bem.block()}> <Form formId={FORM_ID} fields={formFields} action='/api/v1/auth/login' actionMethod='POST' useRedux onComplete={(values, data) => { if (data.accessToken) { dispatch(login(data.accessToken, ROUTE_USERS)); } }} submitLabel='Войти' /> </div> ); }
В этом примере реализована даже логика отправки данных формы на конкретный API endpoint. У Form также есть такие пропсы, как onBeforeSubmit, onAfterSubmit, onComplete, которые вызывают функции соответственно до отправки формы, после отправки и в случае успешного выполнения запроса. Еще есть пропсы для сохранения данных формы в редаксе (useRedux), в адресной строке (addressBar) и в local storage (autoSave). Вообще компонент Form в Steroids довольно функциональный. О нем будет одна из следующих статей.
В работе со списками данных похожая ситуация. Во всех фреймворках есть компоненты List и Pagination, но в большинстве случае это только визуальная составляющая, для работы которой нужно дописывать логику уже в проекте. А в Steroids такая логика написана «под капотом».
Вот пример, как используется List c пагинацией:
<List listId='FlightList' items={flightItems} itemView={ListCard} itemProps={{ bem, }} contentClassName={bem.element('list')} paginationSize={{ defaultValue: 2, sizes: [1, 2, 3], }} pagination={{ defaultValue: 3, }} />
Теперь давайте сравним степень кастомизации компонентов в разных библиотеках. Везде поддерживается кастомизация через различные пропсы для стилей (в каждой библиотеке это будут свои) и через переопределение css-переменных. Библиотека Mantine также предоставляет возможность использовать ее как headless ui-библиотеку, что значит использовать только логику (core-часть), а стили добавлять свои. В Steroids есть возможность переопределять полностью view-часть компонента. Это ещё более глубокая степень кастомизации. Подобный функционал мы встречали только в одной библиотеке — Korus UI от Сбера. Там есть возможность передать view-часть через пропсы, но нет возможности переопределить ее для всех компонентов в проекте (например, для всех InputField).
И напоследок сравним библиотеки по наличию активного сообщества. Этот критерий тоже имеет вес при выборе ui-библиотеки для проекта. У трех из пяти библиотек есть сообщество разработчиков, которое занимается поддержкой, развитием и фиксом багов. А именно у Material UI, Mantine и Steroids.
Какую библиотеку выбрать, конечно, стоит решать в каждом проекте отдельно, так как везде требуется разный функционал. Мы сравнили всего лишь несколько библиотек из тех, что пользуются популярностью у разработчиков, но их существует ещё довольно много.
И в заключение
Изначально фреймворк Steroids не создавался как покоритель всего, но за 8 лет вырос в полноценный продукт.
В 2022 году мы выиграли грант от faise на его развитие. В рамках работ по гранту было реализовано много новых ui-компонентов, а также несколько комплексных компонентов. Мы провели рефакторинг кода, покрыли фреймворк тестами на 70%, написали подробную документацию и запустили сайт с большим набором демоверсий компонентов.
Если вас заинтересовал фреймворк Steroids и вы хотите узнать о нем больше, то вот ссылка на документацию и быстрый старт.
Спасибо за внимание)
ссылка на оригинал статьи https://habr.com/ru/articles/927546/
Добавить комментарий