jtwang7 / React-Note

React 学习笔记
8 stars 2 forks source link

浅谈前端架构模式(MVC / MVP / MVVM / Flux) #33

Open jtwang7 opened 2 years ago

jtwang7 commented 2 years ago

参考文章:

前言

MVC,MVP 和 MVVM 都是常见的软件架构模式(Architectural Pattern),它通过分离关注点来改进代码的组织方式。MVC、MVP 和 MVVM 之间,不同的部分是 C(Controller)、P(Presenter)、VM(View-Model),而相同的部分则是MV(Model-View)。

Model

Model 层用于封装和应用程序的业务逻辑相关的数据以及对数据的处理方法。 假设我们用一个对象来模拟我们的应用,那 Model 层实际上就是存储应用数据和定义应用数据调用方法的地方。

let myapp = {}; // 创建这个应用对象

myapp.Model = function() {
    let val = 0; // 应用数据

    /* 操作数据的方法 */
    this.add = function(v) {
        if (val < 100) val += v;
    };
    this.sub = function(v) {
        if (val > 0) val -= v;
    };
    this.getVal = function() {
        return val;
    };
};

View

View 作为视图层,主要负责数据的展示。View 是 Model 的可视化,表示当前状态的视图。前端 View 负责构建和维护 DOM 元素。

myapp.View = function() {
    /* 视图元素 */
    var $num = $('#num'),
        $incBtn = $('#increase'),
        $decBtn = $('#decrease');

    /* 渲染数据 */
    this.render = function(model) {
        $num.text(model.getVal() + 'rmb');
    };
};

中间商?

通过上述介绍,我们已经知道架构模式中,Model 层实现了数据的存储,View 层实现了数据的呈现,但两者间并没有明确的联系,用户在 View 层的交互行为无法传达到 Model 层,Model 层对于数据的修改也无法同步到 View 层。因此架构模式还需要一个中间结构,来处理 View 层和 Model 层间的连接和交互。

MVC

MVC 秉承数据界面关注点分离的理念,将业务数据(model)与用户界面(view)隔离,用控制器(controller)管理逻辑和用户交互行为。Controller 负责连接 view 和 model,model 的任何变化会同步到view 中,view 的操作会通过 controller 应用到 model 中。

MVC 的工作流程类似于一个三角形循环: V <-> C -> M <-> V View 层的交互行为交由 Controller 层处理,Controller 会调用 Model 层定义的数据操作接口更改数据,Model 层再将更改后的数据改动同步到 View 层。

MVC 存在的问题

  1. Controller 过于臃肿: MVC 模式的业务逻辑主要集中在 Controller,而前端的 View 其实已经具备了独立处理用户事件的能力,当每个事件都流经 Controller 时,这层会变得十分臃肿。
  2. Controller 复用性差: MVC 中 View 和 Controller 一般是一一对应的,捆绑起来表示一个组件,视图与控制器间的过于紧密的连接让 Controller 的复用性成了问题
  3. Model 和 View 没有完全解耦: MVC 中并没有将 Model 和 View 完全解耦,Model 在内部定义了修改数据的方法,同时还对外暴露了它们,这就导致 View 可以越过 Controller 直接操作或监听 Model 内的数据。

MVP

MVP(Model-View-Presenter)是 MVC 模式的改良,和 MVC 的相同之处在于:Presenter 负责业务逻辑,Model 管理数据,View 负责显示。 但是在 MVC 里,View 是可以直接访问 Model 的,MVP 中的 View 并不能直接使用Model,而是通过为 Presenter 提供接口,让 Presenter 去更新Model,更新结果再经过 Presenter 返回给 View 更新视图。

工作流程:V <-> P <-> M

优点

与 MVC 相比,MVP 模式通过解耦 View 和 Model,完全分离视图和模型使职责划分更加清晰;由于 View 不依赖 Model,可以将 View 抽离出来做成组件,它只需要提供一系列接口提供给上层操作。

总结:实现视图与数据的真正解耦。

缺点

Presenter 作为 View 和 Model 之间的“中间人”,除了基本的业务逻辑外,还有大量代码需要对从 View 到 Model 和从 Model 到 View 的数据进行“手动同步”。这将导致 Presenter 显得很重,维护起来会比较困难。而且由于没有数据绑定,如果 Presenter 对视图渲染的需求增多,它不得不过多关注特定的视图,一旦视图需求发生改变,Presenter 也需要改动。

总结:Presenter 缺乏自动同步数据的手段,除了包含大量业务逻辑外,还需要维护大量数据同步的逻辑。

MVVM

MVVM 模式将 Presenter 改名为 ViewModel,基本上与 MVP 模式完全一致。唯一的区别是,它采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然。

工作流程:V <-自动同步-> VM <-> M

优点

MVVM 把 View 和 Model 的同步逻辑自动化了。以前 Presenter 负责的 View 和 Model 同步不再手动地进行操作,而是交给框架所提供的数据绑定功能进行负责。

Flux 模式

参考文章:老生常谈之Flux与Redux思想

前置概念

在 Flux 模式中,存在以下几个角色:

核心思想

数据和逻辑 - 单向流动 Flux 模式中,数据和逻辑是始终是单向流动的: View -> Action -> Dispatcher -> Store -> View 首先用户会在视图 View 交互,产生动作消息 Action,动作消息会传递给调度器 Dispacher,调度器将动作消息发送给每一个数据中心 Store,数据中心再将数据传递给视图。

单向数据流动特点

  1. 集中化管理数据: 常规应用可能会在视图层的任何地方或回调进行数据状态的修改与存储,而在Flux架构中,所有数据都只放在 Store 中进行储存与管理。
  2. 可预测性: 在双向绑定或响应式编程中,当一个对象改变时,可能会导致另一个对象发生改变,这样会触发多次级联更新。对于 Flux 架构来讲,一次 Action 触发,只能引起一次数据流循环,这使得数据更加可预测。
  3. 方便追踪变化: 所有引起数据变化的原因都可由 Action 进行描述,而 Action 只是一个纯对象,因此十分易于序列化或查看。

Flux & React

Flux 架构模式其实在很早之前就出现了,但是为什么近几年才盛行呢? 这是因为单项数据流重渲染视图的代价是很大的,每次数据的更新都调用重渲染,会十分的影响性能,因为直接操作修改 DOM 节点需要巨大的开销。 React 框架提出 Virtual DOM,让数据驱动成为了主流,每次重渲染的是内存上的 Virtual DOM,而 Virtual DOM 本质上是 js 对象,操作修改 js 对象相比于操作 DOM 来说,开销更小。同时,React 还利用了高效率的 diff 算法,保障从重渲染到局部渲染的转换。 综上,抹去了频繁重渲染导致巨大开销的缺点,使得 Flux 这个架构存在更加合理。

Redux

Redux 是基于 Flux 架构思想的一个库的实现,它保留了单向数据流特点的同时,还进一步做了修改。

Redux 三大原则:

  1. 单一数据源 整个应用的State储存在单个Store的对象树中。
  2. State 状态是只读的 您不应该直接修改State,而是通过触发Action来修改它。Action是一个普通对象,因此它可以被打印、序列化与储存。
  3. 使用纯函数进行修改状态 为了指定State如何通过Action操作进行转换,需要编写reducers纯函数来进行处理。reducers通过当前的状态树与动作进行计算,每次都会返回一个新的状态对象。

Redux 与 Flux 的区别

  1. Redux 中只有一个 Store,而 Flux 中有多个 store 来存储应用数据,并在 store 里面执行更新逻辑,当 store 变化的时候再通知 controller-view 更新自己的数据,Redux 是将各个 store 整合成一个完整的 store,并且可以根据这个 store 来得到完整的 state,而且更新的逻辑也不在 store 中,而是在 reducer 中。
  2. Redux 没有 Dispatcher 这个概念。它使用的是 reducer 来进行事件的处理,reducer 是一个纯函数 (preState, action) => newState,在 Redux 应用中,可能有多个 reducer,每一 reducer 来负责维护应用整体 state 树中某一部分,多个 reducer 通过 combineReducers 方法合成一个根 reducer,来维护整个 state。
  3. Redux为不可变数据集。在每次Action请求触发以后,Redux都会生成一个新的对象来更新State,而不是在当前状态上进行更改。