Если вы хотите улучшить общее качество своего кода, возможно, вам захочется аккуратно отделить модели данных от базовых представлений.
Функционально-реактивные фреймворки или библиотеки пользовательского интерфейса, такие как Rimmel.js, которые имеют полную поддержку Observables, позволяют вам определять ваши модели как потоки Observable (например, простые потоки ввода и вывода данных) в дополнение к малоизвестному дизайну. шаблон, который является адаптером событий.
Адаптеры событий помогают сопоставлять любые исходные события (например, MouseEvent, PointerEvent, KeyboardEvent и т. д. DOM) с форматом, фактически используемым вашими моделями данных, поэтому они будут освобождены от этой задачи преобразования и в конечном итоге отделены от Пользовательский интерфейс.
Rimmel упрощает подключение такого потока к DOM:
import { rml } from 'rimmel'; const component = () => { const total = new Subject().pipe( map(x => doSomethingWith(x)), ); return rml`${stream}`; }
Привязка тривиальна: Rimmel подключает события кликов, поступающие от кнопки, непосредственно в ваш наблюдаемый поток, который будет получать экземпляры PointerEvent каждый раз при нажатии кнопки.
Все идет нормально. Что, если вашему потоку необходимо получать данные из нескольких источников и вести себя по-разному в зависимости от каждого?
Давайте создадим простой счетчик с кнопками увеличения и уменьшения, каждая из которых добавляет или вычитает единицу.
import { scan } from 'rxjs'; import { rml } from 'rimmel'; const component = () => { const total = new BehaviorSubject(0).pipe( scan((old, new) => old new, 0), ); return rml`${total}`; }
Это работает, но часть шаблона включает некоторую логику, которая является антишаблоном. В идеале мы должны стремиться к созданию шаблонов без логики, чтобы максимизировать общую тестируемость.
Итак, в Rimmel 1.2 у вас появилась новая функция Event Mappers, которая помогает именно в этом. Они помогают вам сопоставлять события DOM с тем, что нужно вашей модели, поэтому вы можете полностью отделить логику от шаблона. Вот как это работает.
import { map, scan } from 'rxjs'; import { rml, reversePipe } from 'rimmel'; const Inc = reversePipe(map(() => 1)); const Dec = reversePipe(map(() => -1)); const component = () => { const total = new BehaviorSubject(0).pipe( scan((old, new) => old new, 0), ); return rml`${total}`; };
reversePipe — это инновационное дополнение: инструмент создания конвейера, который работает противоположно функции Pipe() в RxJS. В то время как последний применяет преобразования к выходу потока, методversePipe() применяет их к входу.
Таким образом, вы будете уверены, что ваш основной поток subject/BehaviorSubject/Observer/EventListener всегда получает данные в нужных вам форматах, и вы сохраняете свой адаптер как отдельную задачу.
Вы можете использовать любой оператор RxJS в своих обратных конвейерах. Хотите ли вы отфильтровать только определенные события, например, когда пользователь нажимает Enter, а не любую другую клавишу? Просто используйте оператор фильтра:
import { Subject, filter, map } from 'rxjs'; import { rml, inputPipe } from 'rimmel'; const UpperOnEnter = inputPipe( filter((e: Event) => e.key == 'Enter'), map((e: Event) => e.target.value.toUpperCase()), ); const Component = () => { const state = new Subject(); return rml` Type some text and hit Enter
${state}`; };
Что касается модульного тестирования, это небольшое, но полезное дополнение, которое сделает тестирование проще и эффективнее.
Познакомьтесь с картографами событий в действии на Stackblitz
Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.
Copyright© 2022 湘ICP备2022001581号-3