前端mvc mvp mvvm 架构介绍(vue重构项目一)
首先 我们为什么重构这个项目
1:我们现有的技术是前后台不分离,页面上采用esayUI+jq构成的单页面,每个所谓的单页面都是从后台胜场的唯一Id 与前端绑定,即使你找到了那个页面元素,也找不到所在的文件,因为这个id是随机生成的,而页面的id绑定是由后台传回来的Id获得。
2:前后端项目融合在一起,UI框架与jQ使用,对于前端开发人员来说,从最简单的项目启动与调试,都是很繁琐的事情。
3:jsp与后台的架构混杂,文件分离过于复杂,耦合性还很高,针对某个显示出来的页面,我们很难定位到其所在,即使连个路径,都需要从后台获取。
4:开发效率低下,老的技术虽然考验程序员的基本功,但是代码繁杂却又啰嗦,可以看到,在开发时想快速开发 所有的代码都是粘贴复制的。为现在的开发 留下了很多的技术债。
怎么重构这个新的项目呢
由上面的介绍可以了解到,框架的出现是必然的,可以为开发人员提供一个可以快速构建准确且美观简洁的web应用的技术势必受到所有人的追捧,慢慢的前端就形成了现在三大框架 angular vue react 鼎立的局面。
首先 我们不分析这三个框架,我们来看下前端的一些架构上面的基础知识。
前端MVC设计模式
首先 最初这个设计模式是来自后端的java架构,用一张图来表示

这样设计的好处就是,每一层专注于做一件事情,层与层之间保持松耦合,每层做测试也方便,代码的维护性也会变得很好,前端的代码模块化会很明显。
1> mvc:
先来看看《基于MVC的JavaScript Web富应用开发》对MVC的定义——
换句话说,一个事件的发生是这样的过程:
1. 用户和应用产生交互。
2. 控制器的事件处理器被触发。
3. 控制器从模型中请求数据,并将其交给视图。
4. 视图将数据呈现给用户。 我们不用类库或框架就可以实现这种MVC架构模式。关键是要将MVC的每部分按照职责进行划分,将代码清晰地分割为若干部分,并保持良好的解耦。这样可以对每个部分进行独立开发、测试和维护。
关于mvc的框架,如 Embejs angularjs backbonejs Knockoutjs 等等--

模型用来存放应用的所有数据对象。比如,可能有一个User模型,用以存放用户列表、他们的属性及所有与模型有关的逻辑。
模型不必知道视图和控制器的逻辑。任何事件处理代码、视图模板,以及那些和模型无关的逻辑都应当隔离在模型之外。
将模型的代码和视图的代码混在一起,是违反MVC架构原则的。模型是最应该从你的应用中解耦出来的部分。
当控制器从服务器抓取数据或创建新的记录时,它就将数据包装成模型实例。也就是说,我们的数据是面向对象的,任何定义在这个数据模型上的函数或逻辑都可以直接被调用。
视图层是呈现给用户的,用户与之产生交互。在JavaScript应用中,视图大都是由HTML、CSS、JavaScript模板组成的。除了模板中简单的条件语句之外,视图不应当包含任何其他逻辑。 将逻辑混入视图之中是编程的大忌,这并不是说MVC不允许包含视觉呈现相关的逻辑,只要这部分逻辑没有定义在视图之内即可。我们将视觉呈现逻辑归类为“视图助手”(helper):和视图相关的独立的小工具函数。
来看下面的例子,其在视图中包含了逻辑,这是一个范例,平时不应当这样做:
<div>
<script>
function formatDate(data) {
/*....*/
}
</script>
${ formatDate(new Date()) }
</div>
但是在我们的现系统之内,常见的就是 这种将逻辑混淆在视图之内,整个页面看起来就是一个标签的大杂烩,混乱不堪, 难以查找一些逻辑代码,这也是违反了mvc初衷。
再来看下面一段代码,将视觉呈现逻辑剥离出来放入视图助手中,可以让这个应用结构满足mvc。
/* 外部文件 helper.js */
var helper = {};
helper.formateDate = function (date) {
/* */
}
/* template.html 页面 */
<div>
${ helper.formateDate(new Date()) }
</div>
此外,该写法实现了一个功能函数对象,将所有视觉呈现逻辑都包含在helper变量中,这是一个命名空间,可以防止冲突并保持代码清晰、可扩展。
3> 控制器——
控制器是模型和视图之间的纽带。控制器从视图获取事件和输入,对它们(很可能包含模型)进行处理,并相应地更新视图。当页面加载时,控制器会给视图添加事件监听,比如监听表单提交或按钮点击。然后,当用户和你的应用产生交互时,控制器中的事件触发器就开始工作了。
我们用简单的jQuery代码来实现控制器——
var controll={};
(controller.users = function ($) {
var nameClick = function () {
/*...*/
}
$(function() {
$('#view .name').click(nameClick)
});
})(jQuery)
下面再看一张图:

MVC的View直接与Model打交道,Controller仅仅起一个“桥梁”作用,它负责把View的请求转发给Model,再负责把Model处理结束的消息通知View。Controller就是一个消息分发器。不传递数据(业务结果),Controller是用来解耦View和Model的,具体一点说,就是为了让UI与逻辑分离(界面与代码分离)。但是存在的不好的点是 控制层会变的太臃肿,所有视图的变化都在controller层去进行。
一对C-V捆绑起来表示一个ui组件,C直接接受用户输入,并将输入转为相应命令来调用M的接口,对M的状态进行修改,最后通过观察者模式对V进行重新渲染。
2> MVP
先来看一张图 表示下mvp的关系

- Mode-- 数据层 业务逻辑和实体类 存储业务的数据
- View-- 视图层 页面的展示
- p-- Presenter 逻辑层 包括数据和视图层交互 (叫派发器)
虽然在MVC里,View是可以直接访问Model的,但MVP中的View并不能直接使用Model,而是通过为Presenter提供接口,让Presenter去更新Model,再通过观察者模式更新View。
与MVC相比,MVP模式通过解耦View和Model,完全分离视图和模型使职责划分更加清晰;由于View不依赖Model,可以将View抽离出来做成组件,它只需要提供一系列接口提供给上层操作。
先看一个需求

Model层
myapp.Model = function() {
var val = ;
this.add = function(v) {
if (val < ) val += v;
};
this.sub = function(v) {
if (val > ) val -= v;
};
this.getVal = function() {
return val;
};
};
Model层依然是主要与业务相关的数据和对应处理数据的方法。
view层
myapp.View = function() {
var $num = $('#num'),
$incBtn = $('#increase'),
$decBtn = $('#decrease');
this.render = function(model) {
$num.text(model.getVal() + 'rmb');
};
this.init = function() {
var presenter = new myapp.Presenter(this);
$incBtn.click(presenter.increase);
$decBtn.click(presenter.decrease);
};
};
Presenter
myapp.Presenter = function(view) {
var _model = new myapp.Model();
var _view = view;
_view.render(_model);
this.increase = function() {
_model.add();
_view.render(_model);
};
this.decrease = function() {
_model.sub();
_view.render(_model);
};
};
(function() {
var view = new myapp.View();
view.init();
})();
3> mvvm
ViewModel指 "Model of View"——视图的模型。这个概念曾在一段时间内被前端圈热炒,以至于很多初学者拿jQuery和Vue做对比...
MVVM把View和Model的同步逻辑自动化了。以前Presenter负责的View和Model同步不再手动地进行操作,而是交给框架所提供的数据绑定功能进行负责,只需要告诉它View显示的数据对应的是Model哪一部分即可。
这里我们使用Vue来完成这个栗子。
Model
在MVVM中,我们可以把Model称为数据层,因为它仅仅关注数据本身,不关心任何行为(格式化数据由View的负责),这里可以把它理解为一个类似json的数据对象。
var data = {
val:
};
View
和MVC/MVP不同的是,MVVM中的View通过使用模板语法来声明式的将数据渲染进DOM,当ViewModel对Model进行更新的时候,会通过数据绑定更新到View。写法如下:
<div id="myapp">
<div>
<span>{{ val }}rmb</span>
</div>
<div>
<button v-on:click="sub(1)">-</button>
<button v-on:click="add(1)">+</button>
</div>
</div>
ViewModel
ViewModel大致上就是MVC的Controller和MVP的Presenter了,也是整个模式的重点,业务逻辑也主要集中在这里,其中的一大核心就是数据绑定,后面将会讲到。与MVP不同的是,没有了View为Presente提供的接口,之前由Presenter负责的View和Model之间的数据同步交给了ViewModel中的数据绑定进行处理,当Model发生变化,ViewModel就会自动更新;ViewModel变化,Model也会更新。
new Vue({
el: '#myapp',
data: data,
methods: {
add(v) {
if(this.val < ) {
this.val += v;
}
},
sub(v) {
if(this.val > ) {
this.val -= v;
}
}
}
});
整体来看,比MVC/MVP精简了很多,不仅仅简化了业务与界面的依赖,还解决了数据频繁更新(以前用jQuery操作DOM很繁琐)的问题。因为在MVVM中,View不知道Model的存在,ViewModel和Model也察觉不到View,这种低耦合模式可以使开发过程更加容易,提高应用的可重用性。
数据绑定
双向数据绑定,可以简单而不恰当地理解为一个模版引擎,但是会根据数据变更实时渲染。——《界面之下:还原真实的MV*模式》

在Vue中,使用了双向绑定技术(Two-Way-Data-Binding),就是View的变化能实时让Model发生变化,而Model的变化也能实时更新到View。
不同的MVVM框架中,实现双向数据绑定的技术有所不同。目前一些主流的前端框架实现数据绑定的方式大致有以下几种:
- 数据劫持 (Vue)
- 发布-订阅模式 (Knockout、Backbone)
- 脏值检查 (Angular)
我们这里主要讲讲Vue。
Vue采用数据劫持&发布-订阅模式的方式,通过ES5提供的 Object.defineProperty() 方法来劫持(监控)各属性的 getter 、setter ,并在数据(对象)发生变动时通知订阅者,触发相应的监听回调。并且,由于是在不同的数据上触发同步,可以精确的将变更发送给绑定的视图,而不是对所有的数据都执行一次检测。要实现Vue中的双向数据绑定,大致可以划分三个模块:Observer、Compile、Watcher,如图:

Observer 数据监听器
负责对数据对象的所有属性进行监听(数据劫持),监听到数据发生变化后通知订阅者。Compiler 指令解析器
扫描模板,并对指令进行解析,然后绑定指定事件。Watcher 订阅者
关联Observer和Compile,能够订阅并收到属性变动的通知,执行指令绑定的相应操作,更新视图。Update()是它自身的一个方法,用于执行Compile中绑定的回调,更新视图。
数据劫持
一般对数据的劫持都是通过Object.defineProperty方法进行的,Vue中对应的函数为 defineReactive ,其普通对象的劫持的精简版代码如下:
var foo = {
name: 'vue',
version: '2.0'
}
function observe(data) {
if (!data || typeof data !== 'object') {
return
}
// 使用递归劫持对象属性
Object.keys(data).forEach(function(key) {
defineReactive(data, key, data[key]);
})
}
function defineReactive(obj, key, value) {
// 监听子属性 比如这里data对象里的 'name' 或者 'version'
observe(value)
Object.defineProperty(obj, key, {
get: function reactiveGetter() {
return value
},
set: function reactiveSetter(newVal) {
if (value === newVal) {
return
} else {
value = newVal
console.log(`监听成功:${value} --> ${newVal}`)
}
}
})
}
observe(foo)
foo.name = 'angular' // “监听成功:vue --> angular”
上面完成了对数据对象的监听,接下来还需要在监听到变化后去通知订阅者,这需要实现一个消息订阅器 Dep ,Watcher通过 Dep 添加订阅者,当数据改变便触发 Dep.notify() ,Watcher调用自己的 update() 方法完成视图更新。
写着写着发现离主题越来越远了。。。数据劫持就先讲这么多吧~对于想深入vue.js的同学可以参考勾三股四的Vue.js 源码学习笔记
总结
前端mvc mvp mvvm 架构介绍(vue重构项目一)的更多相关文章
- 实例说明MVC,MVP,MVVM架构
很早就知道有这三个概念,但是一直都不清楚是怎么回事,在网上搜索,都是泛泛而谈,没有具体例子,新手是看不懂的,直到找到这篇文章,我对这三个架构有了更清楚的了解. 从一个简单的例子去研究这三个架构. 注意 ...
- 热门前沿知识相关面试问题-MVC/MVP/MVVM架构设计模式面试问题详解
MVC[最常用]: MVC的定义:M:业务逻辑处理.[业务MODEL]V:处理数据显示的部分.[如xml布局文件]C:Activity处理用户交互的问题.[也就是Activity在MVC中扮演着C的角 ...
- Android App的设计架构:MVC,MVP,MVVM与架构经验谈
相关:http://www.cnblogs.com/wytiger/p/5996876.html 和MVC框架模式一样,Model模型处理数据代码不变在Android的App开发中,很多人经常会头疼于 ...
- 浅析前端开发中的 MVC/MVP/MVVM 模式
MVC,MVP和MVVM都是常见的软件架构设计模式(Architectural Pattern),它通过分离关注点来改进代码的组织方式.不同于设计模式(Design Pattern),只是为了解决一类 ...
- Android App的设计架构:MVC,MVP,MVVM与架构AAAAA
1. 架构设计的目的1.1 通过设计使程序模块化,做到模块内部的高聚合和模块之间的低耦合.1.2 这样做的好处是使得程序在开发的过程中,开发人员只需要专注于一点,提高程序开发的效率,并且更容易进行后续 ...
- [转]MVVM架构~mvc,mvp,mvvm大话开篇
MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负 责显示.作为一种新的模式,MVP与MVC有着一 ...
- android MVC && MVP && MVVM分析和对比
相关:http://www.cnblogs.com/wytiger/p/5305087.html 出处http://blog.csdn.net/self_study,对技术感兴趣的同鞋加群544645 ...
- MVC, MVP, MVVM比较以及区别(上)
MVC, MVP和MVVM都是用来解决界面呈现和逻辑代码分离而出现的模式.以前只是对它们有部分的了解,没有深入的研究过,对于一些里面的概念和区别也是一知半解.现在一边查资料,并结合自己的理解,来谈一下 ...
- MVC, MVP, MVVM比较以及区别
MVC, MVP和MVVM都是用来解决界面呈现和逻辑代码分离而出现的模式.以前只是对它们有部分的了解,没有深入的研究过,对于一些里面的概念和区别也是一知半解.现在一边查资料,并结合自己的理解,来谈一下 ...
随机推荐
- java基础(3)--8种基本类型
八种基本类型 数据类型分为两大类:基本类型,引用类型 基本类型有8种,除了基本类型(8种)以外任何类型都是引用类型.如: String 是引用类型 基本类型有8种(首字母是小写): 整数:byte, ...
- hibernate的多对多配置
Teacher.java package com.xiaostudy.domain; import java.util.HashSet; import java.util.Set; /** * Tea ...
- tensorflow笔记:使用tf来实现word2vec
(一) tensorflow笔记:流程,概念和简单代码注释 (二) tensorflow笔记:多层CNN代码分析 (三) tensorflow笔记:多层LSTM代码分析 (四) tensorflow笔 ...
- int(3)与int(11)的区别
注意:这里的M代表的并不是存储在数据库中的具体的长度,以前总是会误以为int(3)只能存储3个长度的数字,int(11)就会存储11个长度的数字,这是大错特错的.其实当我们在选择使用int的类型的时候 ...
- 没有服务器,关于angular路由访问静态页面chrome报错的问题
这个找不到html,报错因为没有xhr,但是在火狐下没有问题的. 比如说ajax,直接写路径的话,我们的chrome也是不支持的,火狐可以的.
- JavaScript中的两个“0” -0和+0
JavaScript中的两个“0”(翻译) 本文翻译自JavaScript’s two zeros JavaScript has two zeros: −0 and +0. This post e ...
- Node.js小白开路(一)-- console篇
在所有内容的学习之中我们经常首先要接受到的常常很大一部分为命令行或是工具的内容展示,console内容为node.js在命令行中答应数据内容的一个途径. Console是nodejs中的元老级模块了. ...
- 【spark】持久化
Spark RDD 是惰性求值的. 如果简单地对RDD 调用行动操作,Spark 每次都会重算RDD 以及它的所有依赖.这在迭代算法中消耗格外大. 换句话来说就是 当DAG图遇到转化操作的时候是不求值 ...
- 【scala】apply和update
我们在使用scala的时候经常会用到对象的apply方法和update方法. 虽然我们表面没有察觉,但是实际上两个方法都会遵循相关约定被调用. apply apply方法的约定:用括号传递给变量(对象 ...
- 蓝牙(cc2540) 协议栈 学习一
---------------------------------------------------------- app ------------------------------------- ...