Автор оригинала: FreeCodeCamp Community Member.
Фабио Хироки
В текущей экосистеме разработки программного обеспечения не удивительно, что архитектура моделей-контроллера (MVC) не имеет большой репутации. Общие альтернативы набирали популярность, такие как модельный вид-ведущий (MVP) и модель ViewModel (MVVM).
Как мобильный разработчик, я попробовал архитектуру MVP. И на самом деле у меня был лучший опыт из-за разделения опасений и улучшенной жизнеспособностью, предоставляемой этой архитектурой. Но это не предлагает шаблон для потока данных (например, потока или redux), и я чувствовал как-то недовольные этим. Я задавался вопросом, есть ли способ минимизировать ошибки и обеспечить лучший опыт разработчика.
Model-View-intent (MVI)
Первая концепция, которая привлекла мое внимание, была реализация моделей-мнения (MVI) на Android, предложенный MOSBY библиотека. Я решил прочитать его код и попытаться понять его принципы.
MOSBY выглядел как отличная библиотека, особенно потому, что его создатель тщательно документировал свою мотивацию и опубликовал примеры его блог . Но, к сожалению, Мосби казался слишком сложным. У него была крутая обучение кривой и было не именно то, что я пытался найти – и представлял только небольшое постепенное улучшение от MVP.
Концепция MVI сначала не была представлена MOSBY, а скорее под названием веб-каркас Cycle.js. . Поэтому я решил изучить основы. К моему удивлению, Cycle.js сделал меня как идею MVI и хочу попробовать. В основном потому, что рамки очень маленькие и простые.
Это основные принципы МВИ, и почему они имеют большое значение:
- Чисто реактивный: Это делает его намного проще координировать асинхронные задачи и приносит все Преимущества от декларативного программирования. В случае Cycle.js это делает ваш Посмотреть Проверь. Как мы собираемся увидеть ниже, вид становится просто распространенным наблюдаемый Отказ
- Однонаправленные данные поток: В MVI данные следует по прямому пути намерение , модель и Посмотреть . Я буду обсуждать это подробно в следующем разделе. Но на данный момент это означает, что вы, как разработчик, должны научиться организовать свой код, чтобы использовать этот шаблон. После того, как вы преодолеете кривую обучения, ваше приложение становится легче понять. Каждая функция в вашем приложении следует за тем же рецептом.
- Просмотр слоя представлен одним объектом, моделью : весь Вид Состояние представлено уникальным источником правды, в том числе Загрузка и ошибка состояния. Это означает, что вы должны посмотреть и манипулировать одно место, чтобы отобразить вид правильно.
Подробнее о дизайне MVI дизайна и преимуществ описаны в Эта статья Cycly.js создатель, а также в Эта статья Отказ Я рекомендую вам прочитать оба, чтобы иметь лучшее понимание, даже если у вас нет фона в веб-разработке.
МВИ в реальном применении
После того, как я получил краткое понимание MVI, я решил построить приложение, используя Cycle.js, чтобы проверить его преимущества практическим путем. Приложение, которое я создал, предоставляет исходный список символов, а затем выполняет запросы на поиск на API звездных войн Когда вы вводите что-то в входном тексте. Вы можете увидеть код в этом Репозиторий Отказ
Основная структура применения Cycle.js является абстракция концепции человеко-компьютерного взаимодействия. Это представлено одной функцией, когда любое внешнее взаимодействие передается в виде параметра функции (обычно называемая «источниками»), а «человеческий» выход – это объект, возвращаемый функцией (обычно называемой «раковинами»).
В нашем приложении это представлено методом «Приложение» в файле «App.js». Код, помещенный между входом и выходом, преобразует «источники» в Намеренные наблюдаемые , который преобразуется в модель наблюдаемый. Последнее затем превращается в Посмотреть наблюдаемый который возвращается внутри объекта «мойки».
export function App (sources) {// ...
return sinks;}
Мы построим каждый слой постепенно в том же порядке, что и данные должны течь.
Намерение
Объект намерения содержит наблюдается генерируется из объекта «источников». Он представляет собой намерение пользователя при взаимодействии с приложением. В нашем приложении пользователь может сделать две вещи:
- Введите поисковый термин, набрав текст ввода
- Получать данные персонажей из API
const intents = { receiveCharacterList: sources.HTTP.select('api').flatten(), changeSearchTerm: sources.DOM.select('#search.form-control') .events("input") .map(ev => ev.target.value) .startWith('')}Вам не нужно беспокоиться, если вы не понимаете, что недвижимость к получению. напряженность объект. На данный момент, чтобы понять концепцию MVI, вам просто нужно понять это: изменение поощрения Получает новый наблюдаемый Всякий раз, когда пользователь набирает что-то на входе, который имеет идентификатор «Search.Form-Control». «По умолчанию он начал с пустой строки.
Модель
Модель Как я уже упоминал выше, является представлением текущего Вид штат. Это зависит только от Стенды объект.
const model = Observable.combineLatest( intents.receiveCharacterList, intents.changeSearchTerm) .map((combined) => {const [response, searchTerm] = combined
return { characters: response.body.results, searchTerm: searchTerm }; }) .startWith({ characters: [{name: 'Loading…'}], searchTerm: '' });Здесь мы объединяем наблюдаемые содержащий ответ API с наблюдаемым содержащий строка Напечатано. Результатом является новый наблюдаемый, содержащий список символов и поисковый термин.
Вид
Вид в Cycle.js не представлен HTML или контроллером слой, так как мы обычно видим в мобильных приложениях. Конфигурация Cycle.js по умолчанию использует библиотеку под названием Цикл Дом , который может генерировать наблюдаемый из виртуального DOM абстракция .
const view = model.map((state) => { const list = state.characters.map( character => { return tr(td(character.name)); }); return div(".card", [ div('.card-header', [ h4('.title', 'Star Wars Character Search'), input('#search.form-control', {props: {type: "text", placeholder: "Type to search", value: state.searchTerm}}) ]), div('.card-content .table-responsive',[ table('.table', [ thead(tr(th(h5('Name')))), tbody(list) ]) ]) ]);});Как я уже упоминал выше, посмотреть зависит только на модель. Он генерирует HTML-таблицу для перечисления символов, и он заполняет Вход с типизированной строкой.
В конце нашей функции «приложения» представление является частью возвращенного объекта «мойки». «Раковины» также должны содержать конфигурацию HTTP-запроса на API:
return { DOM: view, HTTP: intents.changeSearchTerm.map( searchTerm => { return { url: 'https://swapi.co/api/people/?search=' + searchTerm, category: 'api', } })};Устройство тестирования представления
Учитывая, что вид Представительство – это просто функция модели , Мы можем легко написать тесты подразделения для него. Во-первых, я извлек взгляд Создание в методе и переместила его в отдельный файл. Это позволило мне использовать его в приложении и в тестах. Тогда я использовал Chai-Virtual-Dom Пакет для сравнения двух просмотров Отказ
Испытания, которые я реализовал, следуют этой базовой структуре:
- Создать Макет Модель состояния, которую мы хотим проверить.
- Используйте Вид Функция, передавающая созданную макет генерировать свой вид.
- Assert, если созданный вид равно ожидаемому представлению.
В этом приложении я создал два простых тестовых случая:
- Когда приложение загружает данные API, представление следует отобразить Загрузка штат:
const model = Observable.of({ characters: [{name: 'Loading…'}], searchTerm: ''});const view = view(model);
const expected = div(".card", [ div('.card-header', [ h4('.title', 'Star Wars Character Search'), input('#search.form-control', {props: {type: "text", placeholder: "Type to search"}}) ]), div('.card-content .table-responsive',[ table('.table', [ thead(tr(th(h5('Name')))), tbody([ tr(td('Darth Vader')), tr(td('Darth Maul')), ]) ]) ]) ]);expect(view).to.look.exactly.like(expected);
- Когда приложение получило данные персонажей из API, представление следует показать это:
const model = Observable.of({ characters: [{name: 'Darth Vader'}, {name: 'Darth Maul'}], searchTerm: 'darth'});const view = view(model);
const expected$ = div(".card", [ div('.card-header', [ h4('.title', 'Star Wars Character Search'), input('#search.form-control', {props: {type: "text", placeholder: "Type to search"}}) ]), div('.card-content .table-responsive',[ table('.table', [ thead(tr(th(h5('Name')))), tbody([ tr(td('Darth Vader')), tr(td('Darth Maul')), ]) ]) ]) ]);expect(view).to.look.exactly.like(expected);
Заключение
Я получил отличное первое впечатление о архитектуре Model-View-Amentent. Код выглядит более организованным и проще понять, поэтому он предоставляет опыт приятного разработчика. Связь между объектом и его обязанностями уже предопределены, поэтому вам не нужно делать слишком много решений при программировании.
В конце концов, MVI не принимает много усилий для изучения и, похоже, является лучшим выбором при сравнении его с MVP.
Как насчет Cycle.js? Я еще не уверен на 100%, что могу начать строить производственное приложение, используя Cycle.js. Я думаю, что мне нужно исследовать рамки, чтобы оценить свои реальные возможности, такие как создание маршрутов или системы аутентификации.
Вам понравилась эта статья? Если это так, пожалуйста, дайте мне немного хлопья, чтобы все больше людей видят это. Спасибо!
Оригинал: “https://www.freecodecamp.org/news/exploring-cycle-js-and-model-view-intent-ada5ed82da22/”