by Fabio Hiroki

由Fabio Hiroki

让我们探索Cycle.js和Model-View-Intent的好处 (Let’s explore the benefits of Cycle.js and Model-View-Intent)

In the current software development ecosystem, it's not surprising that Model-View-Controller (MVC) architecture doesn’t have a great reputation. Common alternatives have been gaining popularity, such as Model-View-Presenter (MVP) and Model-View-ViewModel (MVVM).

在当前的软件开发生态系统中,模型-视图-控制器(MVC)架构没有很高的声誉也就不足为奇了。 常见的替代方法已经越来越流行,例如Model-View-Presenter(MVP)和Model-View-ViewModel(MVVM)。

As a mobile developer, I tried the MVP architecture. And in fact, I had a better experience because of the separation of concerns and improved testability provided by this architecture. But it doesn't propose a pattern for data flow (like Flux or Redux), and I felt somehow dissatisfied by this. I wondered if there’s a way to minimize the bugs and provide a better developer experience.

作为移动开发人员,我尝试了MVP架构。 实际上,由于关注点的分离和此体系结构提供的改进的可测试性,我获得了更好的体验。 但这并没有提出数据流模式(例如Flux或Redux),对此我感到有些不满意。 我想知道是否有一种方法可以最大程度地减少错误并提供更好的开发人员体验。

模型视图意图(MVI) (Model-View-Intent (MVI))

The first concept that caught my attention was the Model-View-Intent (MVI) implementation on Android proposed by the Mosby library. I decided to read its code and try to understand its principles.

引起我注意的第一个概念是Mosby库在Android上实现的Model-View-Intent(MVI)实现。 我决定阅读其代码并尝试了解其原理。

Mosby looked like a great library, especially because its creator thoroughly documented its motivation and published examples on his blog. But unfortunately Mosby seemed too complex. It had a steep learning curve and wasn’t exactly what I was trying to find — and represented only a small incremental improvement from MVP.

莫斯比看起来像一个很棒的图书馆,特别是因为它的创建者彻底记录了它的动机并将其示例发布在他的博客上 。 但是不幸的是,莫斯比似乎太复杂了。 它的学习曲线很陡,并不是我要尝试找到的东西,并且仅代表MVP的一小部分增量改进。

The MVI concept wasn’t first introduced by Mosby, but rather by a web framework called Cycle.js. So I decided to learn the basics. To my surprise, Cycle.js made me like the MVI idea and want to give it a try. Mostly because the framework is very small and simple.

MVI概念不是由Mosby最初引入的,而是由称为Cycle.js的Web框架引入的 。 因此,我决定学习基础知识。 令我惊讶的是,Cycle.js使我喜欢MVI的想法,并想尝试一下。 主要是因为该框架非常小而简单。

These are the basic principles of MVI, and why they have great value:

这些是MVI的基本原理,以及为什么具有很高的价值:

  • Purely reactive: this makes it much easier to coordinate asynchronous tasks, and brings all the benefits from declarative programming. In the case of Cycle.js, it makes your view testable. As we're going to see below, the view becomes just a common observable.

    纯响应式:这使协调异步任务变得更加容易,并带来了声明式编程的所有好处 。 就Cycle.js而言,它使您的视图 可测试的。 正如我们将在下面看到的那样,该视图只是一个普通的可观察到的

  • Unidirectional data flow: in MVI, the data follows a straight path of intent, model, and view. I will discuss this in detail in the next section. But for now, this means that you as a developer must learn how to organize your code to use this pattern. Once you overcome the learning curve, your application becomes easier to understand. Every feature on your app follows the same recipe.

    单向数据流:在MVI中,数据遵循intentmodelview的直线路径。 我将在下一节中详细讨论。 但是就目前而言,这意味着您作为开发人员必须学习如何组织代码以使用此模式。 一旦克服了学习曲线,您的应用程序将变得更易于理解。 应用程序上的每个功能都遵循相同的配方。

  • The view layer is represented by a single object, the model: the entire view state is represented by an unique source of truth, including the loading and error states. This means that you have to look at and manipulate one place in order to display the view correctly.

    视图层由单个对象模型表示 :整个视图状态由唯一的真实来源表示,包括加载错误状态。 这意味着您必须查看并操纵一个位置才能显示视图 正确地。

More details about MVI design and advantages are described in this article by Cycle.js’ creator and also in this article. I recommend that you read both to have a better understanding even if you don’t have a background in web development.

Cycle.js的创建者在本文中以及本文中都描述了有关MVI设计和优势的更多详细信息。 我建议您阅读这两本书以更好地理解,即使您没有Web开发背景。

实际应用中的MVI (MVI in a real application)

After I gained a brief understanding of MVI, I decided to build an application using Cycle.js to verify its benefits in a practical way. The app I built provides an initial list of characters and then performs search requests on Star Wars API when you type something in the input text. You can see the code in this repository.

在对MVI有了一个简短的了解之后,我决定使用Cycle.js构建一个应用程序,以实际方式验证其优势。 我构建的应用程序提供了一个初始字符列表,然后在输入文本中键入内容时对Star Wars API执行搜索请求。 您可以在此存储库中查看代码。

The main structure of a Cycle.js application is an abstraction of the concept of a human-computer interaction. This is represented by a single function where any external interaction is passed as a function parameter (usually called "sources"), and the "human" output is the object returned by the function (usually called "sinks").

Cycle.js应用程序的主要结构是人机交互概念的抽象。 这由单个函数表示,其中任何外部交互都作为函数参数(通常称为“源”)传递,而“人类”输出是函数返回的对象(通常称为“接收器”)。

In our application, this is represented by the "App" method in the "app.js" file. The code placed between the input and the output will transform the "sources" into an intent observable, which is transformed into a model observable. The latter is then transformed into a view observable which is returned inside the "sinks" object.

在我们的应用程序中,这由“ app.js”文件中的“ App”方法表示。 放置在输入和输出之间的代码将把“源”转换成可观察意图 然后将其转换成模型 可观察的。 然后将后者转换为可观察视图 ,该视图在“接收器”对象内部返回。

export function App (sources) {
// ...
return sinks;}

We will build each layer incrementally in the same order as the data should flow.

我们将按照数据流的顺序依次构建每个层。

意图 (Intent)

The intent object contains observables generated from the "sources" object. It represents the user’s intent when interacting with the application. In our application, a user can do two things:

意图对象包含从“源”对象生成的可观察对象。 它表示用户与应用程序进行交互时的意图。 在我们的应用程序中,用户可以做两件事:

  • Enter a search term by typing on the input text通过在输入文本上输入来输入搜索词
  • Receive characters’ data from the API从API接收字符数据
const intents = {  receiveCharacterList: sources.HTTP.select(‘api’).flatten(),
changeSearchTerm: sources.DOM.select(‘#search.form-control’)    .events(“input”)    .map(ev => ev.target.value)    .startWith(‘’)}

You don't need to worry if you don’t understand the receiveCharacterList property of the intents object. For now, to understand the MVI concept, you just need to understand this: the changeSearchTerm receives a new observable whenever the user types something in the input that has an id of "search.form-control." By default it started with an empty string.

您不必担心,如果你不明白的意图对象的receiveCharacterList财产。 现在,要了解MVI概念,您只需了解以下内容:changeSearchTerm 接收新的可观察到的 ,每当用户键入的东西在具有的ID输入“search.form控制”。 默认情况下,它以空字符串开头。

模型 (Model)

The model, as I've mentioned above, is the representation of the current view state. It depends only on the intents object.

模型 ,正如我上面提到的,是当前的视图状态的表示。 它仅取决于intents对象。

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: ‘’ });

Here we are combining the observable containing the API response with the observable containing the string typed. The result is a new observable containing the list of characters and the search term.

这里我们结合了可观察的 包含带有可观察到的API响应 包含字符串 输入。 结果是一个新的可观察值,其中包含字符列表和搜索词。

视图 (View)

The view in Cycle.js isn't represented by HTML or by a controller layer, as we commonly see in mobile applications. The default Cycle.js configuration uses a library called Cycle DOM, which can generate an observable from a Virtual DOM abstraction.

视图 Cycle.js中HTML或控制器未表示 如我们在移动应用程序中常见的那样 默认的Cycle.js配置使用一个称为Cycle DOM的库,该库可以从Virtual 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)      ])    ])  ]);});

As I mentioned above, view depends only on model. It generates an HTML table for listing the characters and it fills the input with the typed string.

正如我上面提到的, 仅取决于型号。 它生成用于列出字符HTML表,并填充输入 与键入的字符串。

At the end of our “App” function, the view is part of the returned “sinks” object. The “sinks” should also contain the configuration of the HTTP request to the API:

在“ App”功能的末尾,该视图是返回的“接收器”对象的一部分。 “接收器”还应包含对API的HTTP请求的配置:

return {  DOM: view,  HTTP: intents.changeSearchTerm.map( searchTerm => {    return {      url: ‘https://swapi.co/api/people/?search=' + searchTerm,      category: ‘api’,    }  })};

单元测试视图 (Unit testing the view)

Given that the view representation is just a function of the model, we can easily write unit tests for it. First, I’ve extracted the view creation into method and moved it to a separate file. This allowed me to use it in the application and in the tests. Then I’ve used the chai-virtual-dom package to compare two views.

鉴于该观点 表示只是模型的功能我们可以轻松地为其编写单元测试。 首先,我提取了视图 创建到方法并将其移动到单独的文件中。 这使我可以在应用程序和测试中使用它。 然后,我使用了chai-virtual-dom软件包来比较两个视图

The tests I’ve implemented follow this basic structure:

我实施的测试遵循以下基本结构:

  1. Create a mock model of the state we want to test.

    创建一个模拟 我们要测试的状态模型。

  2. Use the view function passing the created mock to generate its view.

    使用视图 函数传递创建的模拟 生成其视图。

  3. Assert if the created view is equal to the expected view.

    断言是否创建了视图 等于预期的观点。

In this application I’ve created two simple test cases:

在此应用程序中,我创建了两个简单的测试用例:

  • When the application is loading the API data, the view should display a loading state:

    当应用程序加载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);
  • When the application has received the characters’ data from the API, the view should display it:

    当应用程序从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);

结论 (Conclusion)

I got a great first impression of the Model-View-Intent architecture. Code looks more organized and is easier to understand, so it provides a nicer developer experience. The communication between an object and its responsibilities are already predefined, so you don’t have to make too many decisions when programming.

我对Model-View-Intent体系结构有了很好的第一印象。 代码看起来更有条理,更易于理解,因此它提供了更好的开发人员体验。 对象及其职责之间的通信已经预先定义,因此您在编程时不必做出太多决定。

In the end, MVI doesn’t take a lot of effort to learn and seems to be a better choice when comparing it to MVP.

最后,MVI不需要花费很多精力去学习,并且与MVP相比似乎是更好的选择。

What about Cycle.js? I’m not yet 100% confident that I can start building a production application using Cycle.js. I think I need to explore the framework further to assess its real capabilities, like creating routes or an authentication system.

那Cycle.js呢? 我还没有100%确信我可以开始使用Cycle.js构建生产应用程序。 我认为我需要进一步探索该框架以评估其实际功能,例如创建路由或身份验证系统。

Did you enjoy this article? If so, please give me some claps so more people see it. Thank you!

你喜欢这篇文章吗? 如果是这样,请给我一些鼓掌,以便更多的人看到它。 谢谢!

翻译自: https://www.freecodecamp.org/news/exploring-cycle-js-and-model-view-intent-ada5ed82da22/

让我们探索Cycle.js和Model-View-Intent的好处相关推荐

  1. RxJS/Cycle.js 与 React/Vue 相比更适用于什么样的应用场景?

    RxJS/Cycle.js 与 React/Vue 相比更适用于什么样的应用场景? RxJS/Cycle.js 与 React/Vue 相比更适用于什么样的应用场景? - 知乎 https://www ...

  2. 第15.22节 PyQt(Python+Qt)入门学习:Model/View架构详解

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 一.简介 在PyQt和Qt中,Model/View架构是图形界面开发时用于管理数据和界面展现方式的关 ...

  3. QT Model/View 编程:MVC模型视图编程:实例实现(二)

    目录 样例001:现有模型中使用视图Using views with an existing model 样例002:使用模型索引 样例003:使用模型 样例004:使用模型的多个视图 样例005:委 ...

  4. jquery.cycle.js

    jquery.cycle.js简单用法实例 样式: a{text-decoration: none;} *{margin:0; padding:0;} /*容器设置*/ .player { width ...

  5. Qt Model/View 学习笔记 (四)

     创建新的Models 介绍 model/view组件之间功能的分离,允许创建model利用现成的views.这也可以使用标准的功能 图形用户接口组件像QListView,QTableView和Q ...

  6. Model/View 教程

     说明:这篇博客基本都是翻译于Qt官方的Model/View Tutorial教程,无法理解的地方建议转到原文,同时,由于译者水平有限,如有差错欢迎指出. 原文:http://qt-project ...

  7. Qt学习笔记-----Model/View架构之自定义Model

    Model/View Framework中提供了模型model的抽象基类QAbstractItemModel, 如果需要自定义模型就需要继承这个类并且实现一些必要的函数. 此外,Qt中又提供了QAbs ...

  8. Qt学习笔记-----Model/View架构

    为了实现数据的存储和表现分离,Qt提供了Model/View架构,包括三个部分,分别是模型(Model),视图(View),委托(delegate). Model用于访问底层数据,也就是说为其他组件访 ...

  9. QT Basic 014 Model/View programming (模型、视图编程)

    前言:本文不是纯文本翻译,加入了对概念的理解,纯文本翻译,请看文后的一个链接. Model/View Programming Introduction to Model/View Programmin ...

最新文章

  1. jpa中使用Query判断条件查询
  2. HotSpotOverview.pdf
  3. Java Integer于Int 进行==双等于的内存比较时的一些问题说明
  4. 2017c语言预测,2017计算机二级C语言上机最终预测题
  5. 使用.udl快速测试与数据库的连接并得到连接字符串
  6. windows 禁用ipv6服务_Win10如何关闭IPV6?Win10禁用IPv6的方法
  7. Swagger使用————接口参数注解的使用缺陷
  8. 我国地方大数据政策的扩散模式与转移特征研究
  9. 洛谷——P1023 税收与补贴问题
  10. java 线程修改数据库连接_如何强制Java线程关闭线程本地数据库连接
  11. 基于蒙特卡洛模拟的大规模电动车充电模型
  12. git报错以及解决方法
  13. Python中的any函数
  14. 麻省理工大学线性代数1806(2)消元法及矩阵消元法 矩阵行变换、列变换 置换矩阵 逆矩阵 如沐春风、如饮甘露、醍醐灌顶的线性代数
  15. 2021年中国乳制品产量、营业收入、利润总额及进出口分析[图]
  16. (dfppy)2Ir(NHC)的蓝光/蓝绿光铱配合物|苯基喹啉酯的中性铱配合物-齐岳生物
  17. Go使用qrcode包解析微信和支付宝二维码,生成一个链接(前端拿到链接即可解析成对应的支付二维码)
  18. ps中如何把图片变白底
  19. STM32晶振 选型
  20. pythonidle安装第三方库_在Python IDLE 下调用anaconda中的库教程

热门文章

  1. dj鲜生-05-配置-静态目录-模板目录-后台语言时区
  2. C# MVC中过滤器的简单使用
  3. 单元测试框架之Robolectric踩坑
  4. 移动硬盘提示无法访问使用驱动器中的光盘之前需要格式化,里面的数据怎么恢复...
  5. JS面向对象编程之封装
  6. Tomcat主配置-应用部署
  7. 整理了vue2.0的思维导图
  8. AC日记——Count on a tree bzoj 2588
  9. 开源 免费 java CMS - FreeCMS1.5-数据对象-job
  10. POJ 1811 Prime Test