[JavaScript, ReactJS] 5 причин использовать LesnJs
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
В данной статье будут рассмотрены: библиотека lens-js и её обёртки lens-ts и react-lens. Мы попробуем сравнить их с популярными менеджерами состояний (Redux и MobX/MST), а также объясним преимущества и недостатки.
Стоит отметить, что "Линзы" - это только концепция. Мы же будем рассматривать конкретную реализацию. Поэтому, нужно понимать, что другие библиотеки, реализующие "Линзы" могут оказаться лучше или чуть-чуть похуже.
Что такое линзы"Линзы" - это концепция функционного программирования, позволяющая решать задачи управления состоянием приложения. Внешне, это большой ориентированный граф, где каждый из узлов является интерфейсом (или контроллером) своего сегмента данных, а сам граф - это правило (или агрегатор), описывающее полное состояние приложения."Линзы" реализуются при помощи "геттеров" и "сеттеров", которые хранят процедуры того, каким образом нужно присоединить отдельный узел ко графу.Более подробнее о линзах и библиотеке lens-js можно узнать в WikiСравнение с ReduxRedux прекрасная и функциональная библиотека с низким порогом вхождения, имеет большое комьюнити и пользуется огромной популярностью. Что же, на этом всё, статья закончена, выводы... до свидания...Ну нет, мы собрались не для того, чтобы просто "сложить лапки". Давайте сначала обратим внимание и на слабые стороны Redux, а именно:
- Гиперизбыточность.
- Сложность адаптации новых сотрудников на проекте.
- Плохая переносимость форм (неуниверсальность)
- Производительность
Архитектурно, Redux реализует работу с состоянием в отдельных агрегаторах (reducer), управляемыми особыми действиями - action. Этот код расположен отдельно и обрабатывается независимо от места и времени вызова. Всё это формирует большой аспект применимый ко всему состоянию приложения. Не АОП, но недостатки, свойственные такому подходу имеются (мы же тут, чтобы немного покритиковать?).Во-первых, аспект не может обладать полной информацией о контексте изменений, в связи с чем, для малоразличающихся действий приходится создавать отдельные обработчики. Иногда, различающиеся только одной строчкой.
Часто приходилось видеть, что для асинхронной работы страницы использовались аж целых 5 действий: INIT, LOADING, LOADED, SECCSES, ERROR. Для всего этого создавались "редьюсеры".
Во-вторых, аспект формирует отдельную подархитектуру, со своими особенностями, знания о которой нужно хранить и передавать. Другими словами, умения (даже хорошо) работать с Redux крайне недостаточно.В-третьих, чем "больше" аспект аспект, тем сильнее он способствует сильной зацеплённости и низкой связности сущностей. Это, к примеру, может повредить общей архитектуре приложения (см. GRASP).А как с этим справляются "линзы"?Давайте вспомним, что по сути, "линзы" - это граф, который является правилом агрегации состояния. Если в Redux мы эти правила описывали в "редьюсерах", то "линза" создаёт их автоматически. Работает это по двум принципам:
- Статистический - с высокой вероятностью, мы не будем хаотично менять типы данных и/или способы их обработки, а напротив - постараемся их придерживаться.
- Структурный - если сущность B является подструктурой A, а сущность D подструктурой C, то с высокой вероятностью они сохранят свои отношения, т. е. маловероятно, что B станет подструктурой C, при условии, что B и D не взаимозаменяемы.
Пусь только вероятностно, но это уже позволяет накопить достаточно информации для автоматической сборки состояния. Рассмотрим пример на линзах:
/* Мы хотим получить цвет бантика у котика */
const color = cat.go('ribbon').go('color');
Сложно представить иную структуру для этого. Однако для того чтобы записать новый цвет, мы должны собрать объект обратно. Хорошая новость в том, что получая дочерние узлы, мы сохраняли информацию о структуре. "Линза", как бы, уже подготовила для нас агрегатор для узла color.
/* Теперь поменяем цвет */
color.set('#FF0000');
Готовый объект будет иметь следующий вид:
{ ribbon: { color: '#FF0000' } }
Изначально, мы специально упустили способ получения узла cat. Это только имя переменной, связанной с неким узлом. Мы могли бы взять его из массива или другого поля, которое имело бы семантически-подобную структуру, например:
{
murzik: { ribbon: { color: '#0000FF' } },
pushok: { ribbon: { color: '#FF0000' } }
}
Тогда код стал бы более универсальным:
function setColor(cat, color) {
cat.go('ribbon').go('color').set(color);
}
setColor(cats.go('murzik'), '#FF0000');
setColor(cats.go('pushok'), '#0000FF');
А вот так, мы бы добавили Рыжика с ленточкой, как у Мурзика:
const murzik = cats.go('murzik');
const rizgik = cats.go('rizgik');
rizgik.go('ribbon').set(
murzik.go('ribbon').get()
);
Как видно из примеров, характер манипуляций очень схож и не требует специфических агрегаторов. Весь представленный код - это действительно всё, что потребуется для работы над подобными объектами.
"Линза" не привязывает к автоматизированному способу склейки данных, а только использует его по умолчанию. Вы можете использовать различные "мапперы". Подробнее можно узнать тут.
Целая "Линза", хоть и является графом, но благодаря поддержке принципа "каждый узел главный", не нужно учитывать структуру до узла. Давайте сравним:
/* Код на redux */
const Cat = ({ cat }) => <div>{cat.ribbon.color}</div>;
const mapStateToProps = (state, ownProps) =>
({cat: state.cats[ownProps.name]});
export default connect(Cat, mapStateToProps);
/* Код на линзах */
export const Cat = ({ lens }) => {
const [ cat ] = useLens(lens);
return <div>{cat.ribbon.color}</div>;
}
Из кода видно, что Redux, хоть и позволил нам выбирать конкретного котика, но жёстко привязал компонент к одному списку. Для попытки взять котика из другого списка, нам пришлось бы вводить второе измерение и т. д. Линзы же ожидают другой подход, где необходимый котик будет взят в компоненте родителя, т. е. вектор данных определяется адаптивно, а не хардкодом, а это делает "линзовый" компонент более универсальным и пригодным к работе на других формах, без необходимости его переделывать.В отличие от Redux, линза вызывает перерисовку только тех компонентов, которые были связанны с изменившимся узлом. Это работает даже, если узел хранит объект. С одной стороны, линза тратит ресурсы на просчёт изменений, с другой - пересчёт виртуального DOM и манипуляции с DOM браузера более тяжёлые.Сравнение с MobX/MSTMST позволяет использовать MobX в едином дереве, органично реализуя работу над всем состоянием приложения. Каждый узел - это многофункциональный контроллер, который позволяет организовывать разноплановую работу с данными узла, делать фоновые запросы и... и так далее...
Использование MST похоже на ход "E2-E4", на большую пивную кружку для эспрессо. Если действительно есть необходимость так упиться кофем, то нет никаких претензий. В остальных случаях это кажется очень нагромождённым.
Между "Линзой" и MST можно разглядеть общие моменты. Но есть же и отличия. Первое из них - это отсутствие необходимости создавать каждый контроллер отдельно, т. е. часть процесса построения дерева "Линза" берёт на себя.
/* Код на MST */
export const Ribbon = type
.model("Ribbon", {
color: type.string
})
.actions(self => ({
setColor(value) {
self.color = value;
},
}));
export const Cat = type
.model("Cat", {
ribbon: type.reference(Ribbon)
});
/* Код на линзе (для такого же функционала) */
const cat = cats.go('murzik');
Разумеется, для разных технологий сложно определить точный эквивалент в исходном тексте, вне контекста конкретного приложения, но... это весьма близко...
Весь фокус в том, что "линза" будет ориентироваться относительно структуры данных, а не передаваемого типа. Потому, нет необходимости этот тип указывать явно. Например, если у котика появится ещё и шапочка, то появится и возможность с ней работать.
/* Шапочки ещё нет, но мы хотим узнать, когда она появиться */
cat.go('cap').attach(() => console.log('Cap is here!'));
/* Теперь оденем шапочку */
cat.go('cap').set({ color: 'black' });
/* Увидим в консоли */
> Cap is here!
С другой стороны, если мы определили, что у котика просто не может быть шляпки, то "Линза" не даст ничего сделать.
interface Cat {
ribbon: { color: string };
}
const getCats = (): Lens<Cat[]> => ... /* Как-то получили котиков */
const cat = getCats().go(0);
const cap = cat.go('cap'); // Выдаст ошибку
Недостатком контроллера по умолчанию, в данном случае, будут отсутствие следующих возможностей:
- Расширять контроллер в каждом конкретном случае. Однако и тут срабатывает принцип статистичности. Из множества методов для работы состоянием есть много похожих, например "геттеры" и "сеттеры", определив которые можно покрыть значительную часть функциональности.
- Обеспечение инкапсуляции. Стоит отметить, что инкапсуляция это не инструмент защиты исполняемого кода, а подход к проектированию, т.е. можно просто договориться, какие поля не трогать =)
Стоит отметить, что "линзы", всё же, имеют возможность расширятся в парадигме ООП и приближаться к функциональности к MST. Подробнее можно прочесть тут.
Резюме...ну наконец то!Давайте подведём итоги, добавим ещё несколько общих плюсов и составим список причин, всё же, использовать "Линзы".
- "Линзы" весьма компактны по сравнению с Redux и MobX/MST.
- "Линза" сохраняет (по крайней мере, пытается) производительность приложения.
- "Линза" позволяет создавать универсальные компоненты, независящие от конкретных форм и, даже, технологий. Вы смело можете сделать свой фреймворк - линзы будут работать и там.
- "Линза" умеет в ООП. Может работать с типами данных, а также масштабироваться.
- "Линза" не тянет за собой зависимости (за исключением обёрток). Это очень небольшая и монолитная библиотека.
Постой! Ну есть же "скелеты в шкафу"?Ох, думал не спросите...У "линзы" есть два существенных недостатка, на данный момент.
- Отсутствие большого числа надстроек, промежуточного ПО и смежных библиотек. Однако выходом их этой ситуации может стать комбинирование подходов к организации состояния. Например, в качестве состояния форм можно использовать Redux, а компоненты сделать на "Линзах". Это позволит использовать промежуточное ПО (Saga, например), избавит от избыточности и сделает код более универсальным и переносимым. Мега-комбо!
- Ограничение при серверном "рендеринге". "Линза" потребует специальной оснастки, которую придётся писать самостоятельно. Стоит отметить, что речь идёт только о библиотеке lens-js, а не о всех "линзах" в целом.
Удачи в экспериментах, друзья!
===========
Источник:
habr.com
===========
Похожие новости:
- [JavaScript, ReactJS] Почему иногда React/Redux в текущем состоянии give me creeps
- [Java, IT-компании] Spring MVC vs Spring WebFlux. Что лучше? Объясняем на пингвинах
- [JavaScript, Maps API, ReactJS] Использование mapbox-gl в React и Next.js
- [] Зачем Тинькофф Путешествиям офлайн-режим
- [Высокая производительность, Разработка веб-сайтов, JavaScript, Программирование, Клиентская оптимизация] Оптимизации в вебе — дорого, сложно, и уже не нужно?
- [JavaScript, Программирование, Конференции, Видеоконференцсвязь] Видеочат с возможностью совместного редактирования текста при помощи Twilio Sync (перевод)
- [JavaScript, Разработка мобильных приложений, Разработка под Tizen, Производство и разработка электроники, Мониторы и ТВ] Разработка Tizen-приложений для Samsung Smart TV: полный гайд для Javascript-разработчиков
- [Java] Реактивное программирование со Spring (перевод)
- [Java] Реактивное программирование со Spring, часть 4 R2DBC (перевод)
- [Java] Реактивное программирование со Spring, часть 3 WebFlux (перевод)
Теги для поиска: #_javascript, #_reactjs, #_react, #_lens, #_redux, #_mobx, #_mobxstatetree, #_javascript, #_reactjs
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 14:36
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
В данной статье будут рассмотрены: библиотека lens-js и её обёртки lens-ts и react-lens. Мы попробуем сравнить их с популярными менеджерами состояний (Redux и MobX/MST), а также объясним преимущества и недостатки. Стоит отметить, что "Линзы" - это только концепция. Мы же будем рассматривать конкретную реализацию. Поэтому, нужно понимать, что другие библиотеки, реализующие "Линзы" могут оказаться лучше или чуть-чуть похуже.
Часто приходилось видеть, что для асинхронной работы страницы использовались аж целых 5 действий: INIT, LOADING, LOADED, SECCSES, ERROR. Для всего этого создавались "редьюсеры".
/* Мы хотим получить цвет бантика у котика */
const color = cat.go('ribbon').go('color'); /* Теперь поменяем цвет */
color.set('#FF0000'); { ribbon: { color: '#FF0000' } }
{
murzik: { ribbon: { color: '#0000FF' } }, pushok: { ribbon: { color: '#FF0000' } } } function setColor(cat, color) {
cat.go('ribbon').go('color').set(color); } setColor(cats.go('murzik'), '#FF0000'); setColor(cats.go('pushok'), '#0000FF'); const murzik = cats.go('murzik');
const rizgik = cats.go('rizgik'); rizgik.go('ribbon').set( murzik.go('ribbon').get() ); "Линза" не привязывает к автоматизированному способу склейки данных, а только использует его по умолчанию. Вы можете использовать различные "мапперы". Подробнее можно узнать тут.
/* Код на redux */
const Cat = ({ cat }) => <div>{cat.ribbon.color}</div>; const mapStateToProps = (state, ownProps) => ({cat: state.cats[ownProps.name]}); export default connect(Cat, mapStateToProps); /* Код на линзах */ export const Cat = ({ lens }) => { const [ cat ] = useLens(lens); return <div>{cat.ribbon.color}</div>; } Использование MST похоже на ход "E2-E4", на большую пивную кружку для эспрессо. Если действительно есть необходимость так упиться кофем, то нет никаких претензий. В остальных случаях это кажется очень нагромождённым.
/* Код на MST */
export const Ribbon = type .model("Ribbon", { color: type.string }) .actions(self => ({ setColor(value) { self.color = value; }, })); export const Cat = type .model("Cat", { ribbon: type.reference(Ribbon) }); /* Код на линзе (для такого же функционала) */ const cat = cats.go('murzik'); Разумеется, для разных технологий сложно определить точный эквивалент в исходном тексте, вне контекста конкретного приложения, но... это весьма близко...
/* Шапочки ещё нет, но мы хотим узнать, когда она появиться */
cat.go('cap').attach(() => console.log('Cap is here!')); /* Теперь оденем шапочку */ cat.go('cap').set({ color: 'black' }); /* Увидим в консоли */ > Cap is here! interface Cat {
ribbon: { color: string }; } const getCats = (): Lens<Cat[]> => ... /* Как-то получили котиков */ const cat = getCats().go(0); const cap = cat.go('cap'); // Выдаст ошибку
Стоит отметить, что "линзы", всё же, имеют возможность расширятся в парадигме ООП и приближаться к функциональности к MST. Подробнее можно прочесть тут.
=========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 14:36
Часовой пояс: UTC + 5