基于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 条评论
登录 后参与评论

相关文章

来自专栏CSDN技术头条

有关网页渲染,每个前端开发者都该知道的那点事

【编者按】其实,有关网页渲染的文章很多,但是相关信息比较分散,且论述并不是很完整。如果要想对这个主题有个大致的了解,我们还得学习很多知识。因此,Web开发者Al...

1878
来自专栏张善友的专栏

Cocoa编程学习笔记之MVC

Cocoa 使用了一种修改版本的MVC模式来处理GUI的显示。MVC模式(自1979年以来)已经出现很长时间了,它皆在分离显示用户界面所需的大量任务,并处理用户...

1727
来自专栏IMWeb前端团队

如何在前端编码时实现人肉双向编译

本文作者:IMWeb yisbug 原文出处:IMWeb社区 未经同意,禁止转载 如何在前端编码时实现人肉双向编译 React+flux是目前最火的前...

1735
来自专栏web前端教室

农历2016年最后一次先行者群视频

今天是周日,也是农历2016年最后一次先行者群视频课程,然后就是春节了,提前祝大家春节快乐。下次课程就要在2月才开始。 ? 如上图,从2016年二三月吧,开始到...

1687
来自专栏达摩兵的技术空间

css3媒体查询简介

<link rel="stylesheet" media="min-width=900" />

645
来自专栏听雨堂

JQuery笔记(三) jquery的用途

  近期jquery的学习经历了一些反复:开始时觉得非常强大,比js好用,和css关系密切,一句话:完美。等到把《锋利的JQuery》看完,又有点不知道用它来干...

2099
来自专栏柠檬先生

VUE 入门基础(9)

十一,深入响应式原理    声明响应式属性     由于Vue不允许动态添加根级响应式属性,所以你必须在初始化实例钱声明根级响应式属性,哪怕只有一个空值。 ...

1945
来自专栏xingoo, 一个梦想做发明家的程序员

JQuery向导插件Step——第一个阉割版插件

如果使用过JQuery Steps的朋友一定会发现这个插件有一个缺点,就是页面在第一次进入的时候,会进行一次很明显的DOM重绘——页面会闪一下。 尤其是前端...

2277
来自专栏web编程技术分享

《从案例中学习JavaScript》之酷炫音乐播放器(二)

33113
来自专栏互联网杂技

Angular 1 vs. Angular 2 深度比较

AngularJS 2 尽管还在Alpha阶段,但主要功能和文档已经发布。让我我们了解下Angular 1 和 2 的区别,以及新的设计目标将如何实现。 Ang...

32710

扫描关注云+社区