基于MVC理解React+Redux

我认为MVC模式虽然已经诞生了许多年,也有无数前端框架遵循了MVC模式,但我们在前端开发时,很多时候还是忽略了这个模式蕴含的思想。该思想的核心就是职责分离,这种分离又隐含了“信息专家模式”的意义,直白地说,就是“专业的事情应该交给专业的人去做”。

MVC(Model-View-Controller)的三个角色其实是各司其职:

  • model持有UI要展现的数据
  • View即UI的展现
  • Controller用于控制

以React来说,它就应该只专注于View的呈现,并将这些展现元素封装为Component。这些Component要展现的props可以视为Model所持有的数据。

那么,什么情况下会导致View产生变化呢?从表象上看,似乎引起变化的原因是由于客户端的某种请求或交互操作产生的事件。实则从业务上说,其实就是要改变Model的值,而UI的交互操作不过是对这种变化的界面展现罢了。换言之,View的变化其实应该通过Model的变化来传递。

当我们需要改变View时,一种做法是直接在View上做文章,通过编写针对UI元素的控制逻辑去改变View。另一种做法就是遵循MVC模式,应该通过Controller去改变Model的结构,然后通知View去改变自己(或者理解为View侦听到Model的变化,从而改变自己)。

React结合Redux框架做的正是这样的事情。在设计React Component时,我们需要通过UI的Layout来规划我们的Component,包括Component的分解与组合。呈现Component的过程就可以抽象为一个函数,这个函数接收一个输入对象model,返回一个包裹了HTML元素与Model的DOM结构。如以下伪代码:

const render = (model) => DOM

如果业务逻辑要求操作View的DOM,其实就是对DOM包裹的Model进行操作,例如添加或修改某个<li>,其本质是要添加或修改<li>元素中的值,这个值来自于Model。在Redux中,其实就是发起一个action。

执行action的目的虽然是修改Model,不过在Redux中,我们尽量希望遵循FP的思想设计出所谓的“纯函数”,于是Redux就引入了reducer函数,这个函数要做的事情其实就是对Model进行transform(可以考虑引入immutable.js来存储和操作Modle)。一旦Model对象发生了变化(并不是真正发生了变化,而是产生了一个新的Model),Redux就会通知React Component根据新获得的Model去重新Render。

显然,React扮演的是View的角色,Redux则是Controller,至于Model就是Redux Store中存储的State。我们要从MVC模式的角度去思考React+Redux开发,把代码需要做的每件事情想清楚,明确是谁的职责,如此才不至于在实现时走歪路,不讨好地去编写大量View的控制逻辑,尤其是那些牵涉到parent-child组件的递归关系时,可能会让前端代码炖成一锅粥。

举个实例。

我们要在前端编写一个过滤器,UI展现与控制逻辑类似Logiform,如下图所示:

△ Logiform的过滤器

这个过滤器可以理解为以Condition为根的一个递归嵌套树形结构,枝为Group,而叶为Expression。Group还可以嵌套Group或者Expression。可以添加、删除Group或Expression,也可以调整它们在树中所处的位置。

针对这样的需求,如果我们企图在React Component中直接去操控和管理这些逻辑,就需要考虑Component的父子关系,还需要考虑添加或删除Dom节点对整棵树的影响。

如果我们站在前述MVC模式的角度来考虑过滤器树的呈现与界面控制,其实不过就是针对Condition对象模型的操作罢了。这个时候,我们可以不用去操心DOM节点之间的关系,而是直接用React Component去render模型对象。对象的粗略结构如下所示:

{    "id": 1,    "operator": "and",    "conditions": [        {            "id": 2,            "type": "expression",            "operator": "=",            "fieldId": "11000",            "value": 3        },        {            "id": 3,            "type": "group",            "operator": "or",            "conditions": [                {                    "id": 4,                    "type": "expression",                    "operator": "<=",                    "fieldId": "11001",                    "value": 20                }            ]        }    ]}

由于render是一种只读的操作,要在React Component中去render这样的结构是非常容易的。如上,当我们要删除id为2的Expression时,其实就是去编写一个reducer,将其转换为如下的对象:

{    "id": 1,    "operator": "and",    "conditions": [      
  {            "id": 3,            
  "type": "group",           
   "operator": "or",            
   "conditions": [                {                
       "id": 4              
       "type": "expression",                    
       "operator": "<=",                    
       "fieldId": "11001",                    
       "value": 20                }            ]        }    ]}

render对UI的呈现与控制逻辑完全相同,并不需要再去控制复杂的DOM。

概括下来,React+Redux的主体流程为:

  • 通过action获得model,并将其作为state存储到Store中;
  • 传递给React Component,按照某种设计呈现model数据;
  • 调用action发起update请求,从而调用reducer生成新的state存储到Store中;
  • redux通知React Component重新Render。

这是MVC三种角色各司其职相互协作的结果。

原文发布于微信公众号 - 逸言(YiYan_OneWord)

原文发表时间:2016-08-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏非著名程序员

我在开发微信小程序中用到的几个不错的库

昨天文章中给大家提到过,在今天的文章中,给大家分享一下,我在开发过程中用到的不错的一些微信小程序的开源库。说道做到,今天就给大家分享。 图片裁剪工具 图片裁剪工...

2639
来自专栏河湾欢儿的专栏

响应式布局流式布局

响应式布局:根据不同的尺寸适配 viewport width=device-width 设置视口的宽度等于设备的宽度,如果不设置的话,默认视口的宽度是980...

722
来自专栏梁源的专栏

React Native 用JavaScript开发移动应用 - 思维导图

1374
来自专栏吴老师移动开发

零基础iOS开发-Swift版-视频课程【01】

813
来自专栏九彩拼盘的叨叨叨

浏览器控制台生成简书文集的Markdown格式的列表

最近准备给我在简书写的关于web前端的文章做个导航页。那些文章都放在web前端这个文集。 导航内容的是用Markdown来写,内容形式大概下面这样的

673
来自专栏数据结构与算法

pd_ds中的hash

前言 在c++的STL中,提供了一种hash函数,其用法和map是几乎一样的,但是速度却能快接近一倍 使用方法 需要的头文件 #include<ext/pb_d...

3239
来自专栏前端知识分享

第181天:HTML5——视频、音频

761
来自专栏Kiba518

【我们一起写框架】MVVM的WPF框架之绑定(二)

上一篇我们已经一起编写了框架的基础结构,并且实现了ViewModel反向控制Xaml窗体。

682
来自专栏ytkah

小程序与小游戏使用wx.getUserInfo 接口直接弹出授权框的开发方式将逐步不再支持

各位小程序开发者注意了:从2018年4月30日开始,小程序与小游戏的体验版、开发版调用 wx.getUserInfo 接口,将无法弹出授权询问框,默认调用失败。...

5365
来自专栏我分享我快乐

一线UI设计师对APP切图尺寸换算指导

工作后,很多同学还是会问到app切图尺寸的问题。今天分享android和iOS的换算分析,希望能帮到工作中的你。 1.在Android的显示模式为mdpi的时...

34414

扫码关注云+社区