对付MVC想来小伙伴是不陌生的,但是网上的资源各抒己见…我也整得晕头转向的,可能有前(后)端,有胖(瘦)客户端框架运用,详细还有细微的差异。
If you put ten software architects into a room and have them discuss what the Model-View-Controller pattern is, you will end up with twelve different opinions. --Josh Smith1如果你把10个软件架构师放在一个房间里,让他们谈论模型-视图-掌握器模式是什么,你终极会得到12种不同的不雅观点。
我们这里谈论的MVC和MVVM因此BS架构为根本的java Web中的运用,由于博主只打仗了这方面的,关于网上提到的IOS和一些客户端框架,没有打仗过。本博客也不涉及。以是如果听都没听过java Web的,或者没理解过 Web框架的小伙伴个人觉得这篇博客不太适宜,不太建议连续读下去。
我们先看看MVVM吧!
嘻嘻 ^ _ ^
MVVM是Model-View-ViewModel的简写。它实质上便是MVC的改进版。MVVM 便是将个中的View的状态和行为抽象化,让我们将视图 UI和业务逻辑分开。当然这些事ViewModel已经帮我们做了,它可以取出 Model 的数据同时帮忙处理View中由于须要展示内容而涉及的业务逻辑。MVVM(Model-View-ViewModel)框架的由来便是MVP(Model-View-Presenter)模式与WPF结合的运用办法时发展演化过来的一种新型架构框架。它立足于原有MVP框架并且把WPF的新特性糅合进去,以应对客户日益繁芜的需求变革。2
MVVMupright=1.5 MVVM(Model–view–viewmodel)是一种软件架构模式。 MVVM有助于将图形用户界面的开拓与business logic(业务逻辑)或后端逻辑(数据模型)的开拓分离开来,这是通过置标语言或GUI代码实现的。MVVM的视图模型是一个值转换器, 这意味着视图模型卖力从模型中暴露(转换)数据工具,以便轻松管理和呈现工具。在这方面,视图模型比视图做得更多,并且处理大部分视图的显示逻辑。 视图模型可以实现中介者模式,组织对视图所支持的用例集(Model)的后端逻辑的访问。 3
MVVM 的发展进程MVVM是马丁·福勒的PM(Presentation Model)设计模式的变体。MVVM以相同的办法抽象出视图的状态和行为, 但PM以不依赖于特定用户界面平台的办法抽象出视图(建立了视图模型)。 MVVM和PM都来自MVC模式。 MVVM由微软架构师Ken Cooper和Ted Peters开拓,通过利用WPF(微软.NET图形系统)和Silverlight(WPF的互联网运用衍生品)的特性来简化用户界面的事宜驱动程式设计。 微软的WPF和Silverlight架构师之一John Gossman于2005年在他的博客上揭橥了MVVM。 MVVM也被称为model-view-binder,特殊是在不涉及.NET平台的实现中。ZK(Java写的一个Web运用框架)和KnockoutJS(一个JavaScript库)利用model-view-binder。3
MVC到MVVM 的发展进程二十世纪八十年代施乐帕克实验室提出了MVC的观点,MVC的全称即Model-View-Controller,是模型(model)一视图(view)一掌握器(controller)的缩写“…,它是一种客户端软件开拓框架4,个人认为,实在最初的Java Web来讲,Model2 即Servlet+JSP也是用的这个构造,以是说Model2(MVC)它相对已Model1(Javabean+JSP)来讲,已经实现了View和Model的部分解耦,但是不彻底,如图
view卖力显示,Model卖力供应数据,Controller卖力逻辑的处理,实在现的流程大概是:4
(1)当用户须要发送要求时,首先是在View发送要求,由View将指令传送到Controller里。(2)Controller吸收到指令之后,先完成所须要的业务逻辑,然后哀求Model根据业务逻辑改变状态;(3)Model将新的数据发送给View,View则根据新的数据更新视图,从而用户的要求得到反馈。在MVC框架中,View是可以直接访问Model的(JSP里直策应用JavaBean),这样不可避免的使View里面也须要包括一些业务逻辑,同时还须要Model保持不变,而Model又对应着多个不同的显示(View),以是总体说来便是,在MVC模型里面,Model不依赖View,但是View是依赖于Model的。这样就导致变动View比较困难,且业务无法重用。从而MVC框架的弊端就显现出来4,这也是利用Servlet+JSP的弊端。前后端没有解耦,Model与View没有彻底解耦。
为理解决MVC框架中View和Model联系紧密的问题,开拓者研究开拓了MVP模式,MVP即Model-View-Presenter,即把MVC中的Controller换成了Presenter,目的便是为了完备割断View跟Model之间的联系,在MVP模式中,View卖力视图的显示,Model卖力供应数据,Presenter则紧张卖力逻辑业务的处理。4
有些SSM+JSP的开拓办法也是基于这种,我之前的公司就这样写,前后端不分离利用的JSP,但是交互全是Ajax,通报的全是JSON,也没有返回ModelAndView,个人觉得这里实在是利用了MVP的模式。以前后端不分离的办法丢弃模板引擎的做事端渲染,追求前后端分离中彻底解耦了View和Model。看上去怪怪的,实在有时候项目开拓更多的是和业务、体量、本钱、效益等有关系,综合考虑,选择最得当,不一定要按照常规的构建办法考虑,比如正常思考可能不分离是为了做事端渲染,首屏快载,SEO等,分离是为了降落做事器压力,接口复用,前后端事情职责解耦.
对付SSM+模板引擎的开拓办法
如何是返回Modelandview的话,那缺陷便是后端路由,前后端没有彻底解耦,优点便是做事端渲染,返回的是全体构建好的页面.如果返回JSON的话,那优点便是前后端彻底解耦,接口复用,但是没有利用模板引擎的做事端渲染。如果体量很大,那前后端是两个人写,那利用Modelandview的办法就很麻烦,须要接口折衷,而且事情职责不清晰。会摧残浪费蹂躏好多韶光。JSON就方便很多。如果体量不是他大,前真个东西也不是特殊多,考虑本钱问题,前后端一个人写,那Modelandview就很得当,节省了接口折衷,对接等韶光本钱问题。在MVP框架中,View无法直接再与Model交互,View和Model之间的通信都是通过Presenter进行完成的,所有的交互都在Presenter内部发生,即由Presenter充当了View和Model的桥梁,做到View-Model之间通信的完备隔离。Presenter完备把Model和View进行分离,将紧张的程序逻辑放在Presenter里实现。4
Presenter与View也是没有直接干系联的,而是通过已定义的接口进行交互,从而使得在变更View的时候可以保持Presenter的不变,即担保了Presenter的可重用性(接口的复用性),同时也办理了MVC框架中的View和Model关联紧密的问题。4
这样之后,对付Web项目来讲,前后端都是通过数据进行交互,那路由怎么处理,前端只能实现大略一部分跳转,涉及到繁芜的须要通过Controller(Presenter)来处理的路由怎么处理,或者带状态的路由如何跳转,即Controller无法掌握利用那个View。个人觉得,Web系统来讲这个时候完备的前后端分离可能不是适宜所有项目,而且分离之后留给前端要办理的问题可能也不是能很好的办理。以是这个时候…
有个叫Rod Johnson带领一帮人搞出的SpringMVC,不像桌面运用的MVC, 这里的Model没法给View 发关照。5也不像MVP, 这里的Controller可以掌握View来实现路由。即前后后端没有分离,但是将原来的View的构建解耦了。由模板和数据构成:
public class MyGlobalException { @ExceptionHandler(MaxUploadSizeExceededException.class) public ModelAndView customException(MaxUploadSizeExceededException e) { ModelAndView mv = new ModelAndView("javaboy"); mv.addObject("error", e.getMessage()); return mv; }}
即降落了View和Model耦合,同时又实现了后端路由。
对付大型项目而言,前真个东西原来越多,造成做事真个压力越来越大,而且由于MVP的涌现,逐渐向前后端分离靠拢,分离之后,View分担做事真个压力,或者说是浏览器分担了做事器压力,包括页面渲染,路由等问题,这时侯MVVM涌现了…(这里是自己猜的,没找到干系资料)
MVVM框架便是前后端分离框架发展史上的一次思想的完备变革。它是将数据模型双向绑定的思想作为变革的核心,即View的变动,自动反响在ViewModel上面,而ViewModel的变动也会随即反响在View上面,从而实现数据与模型的双向绑定。4
在MVVM框架中,View用于发送用户的交互要求,之后将用户要求转交给ViewModel,ViewModel即可根据用户要求操作Model数据更新,待Model数据更新完毕,便会关照ViewModel数据发生了变革,然后ViewModel就会即刻更新View数据,完成视图的更新,从而完成用户的要求。4
虽然MVVM框架和之前的MVC、MVP模式的目的相同,即完成视图(View)和模型(Model)的分离,但它却有着明显的上风。4
首先,MVVM框架中的View完备可以独立于Model发生变革和修正,彻底解耦,View发生变革时Model可以不变,同样,当Model发生变革时View也可以不变革,并且一个ViewModel可以绑定到多个不同的View上面,这就表示了MVVM框架的低耦合性。其次,绑定在一个ViewModel上面的多个View都可以利用ViewModel里面的视图逻辑,完成了框架可重用性的特性。除此之外,MVVM框架还具有可独立开拓、可测试等特性,把框架浸染发挥到最大化,也因此成为了开拓者们青睐的框架。。对付MVVM这种模式紧张用于构建基于事宜驱动的 UI 平台,对付前端开拓领域中数据与界面相稠浊的情形特殊适用6,个中
Model 仅仅只是代表运用程序所需的数据信息,它不关注任何行为;View 是软件中与用户进行直接交互的部分,它须要相应 ViewModel 的事宜并格式化数据,不卖力掌握运用的状态;ViewModel 用于封装业务逻辑层,这点类似于 MVC 模式中的掌握器,它掌握View的很多显示逻辑,它可以把数据模型的变革通报给视图,也可以把视图中数据的变革通报给数据模型,即在 Model 和View 之间建立了双向绑定。Vue与MVVM我第一次看到MVVM是由于Vue,相信好多小伙伴也是Vue认识MVVM架构模式。Vue官网中讲到:虽然没有完备遵照 MVVM 模型,但是 Vue 的设计也受到了它的启示。因此在文档中常常会利用 vm (ViewModel 的缩写) 这个变量名表示组件实例
通过双向数据绑定连接视图层和数据,而实际的界面 UI 操作(DOM 操作)被封装成对应的指令(Directives)和过滤器(Filters)
MVVM事理:7实现数据绑定的做法有大致如下几种:
脏值检讨(angular.js): angular.js 是通过脏值检测的办法比对数据是否有变更,来决定是否更新视图,最大略的办法便是通过 setInterval() 定时轮询检测数据变动,angular只有在指定的事宜触发时进入脏值检测.DOM事宜,譬如用户输入文本,点击按钮等。( ng-click)XHR相应事宜 ($http )浏览器Location变更事宜 ( $location )Timer事宜( $timeout , $interval )实行 $digest() 或 $apply()数据挟制(vue.js):数据挟制,指的是在访问或者修正工具的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修正返回结果。大略地说,便是当我们触发函数的时候 动一些手脚做点我们自己想做的事情,也便是所谓的 "挟制"操作在Vue中实在便是通过Object.defineProperty来挟制工具属性的setter和getter操作,并“种下”一个监听器,当数据发生变革的时候发出关照:Object.defineProperty(obj,prop,descriptor)参数:obj:目标工具prop:须要定义的属性或方法的名称descriptor:目标属性所拥有的特性可供定义的特性列表:value:属性的值writable:如果为false,属性的值就不能被重写。get: 一旦目标属性被访问就会调回此方法,并将此方法的运算结果返回用户。set:一旦目标属性被赋值,就会调回此方法。configurable:如果为false,则任何考试测验删除目标属性或修正属性性以下特性(writable, configurable, enumerable)的行为将被无效化。enumerable:是否能在for…in循环中遍历出来或在Object.keys中列举出来。Proxy数据代理:Proxy 可以被认为是Object.defineProperty() 的升级版。外界对某个工具的访问,都必须经由这层拦截。因此它是针对 全体工具,而不是 工具的某个属性。var data = {name:'test'}Object.keys(data).forEach(function(key){ Object.defineProperty(data,key,{ enumerable:true, configurable:true, get:function(){ console.log('get'); }, set:function(newValue){ console.log('监听到数据发生了变革'); document.getElementById(‘myText’).value=newValue; } })});document.getElementById(‘myText’).addEventListener(‘keyup’,function(e){ data.name=e.target.value; // 监听 View 的变革,同步更新 Model});data.name //掌握台会打印出 “get”data.name = 'hxx' //掌握台会打印出 "监听到数据发生了变革"
var arr = [1,2,3]var handle = { //target目标工具 key属性名 receiver实际接管的工具 get(target,key,receiver) { console.log(`get ${key}`) // Reflect相称于映射到目标工具上 return Reflect.get(target,key,receiver) }, set(target,key,value,receiver) { console.log(`set ${key}`) return Reflect.set(target,key,value,receiver) }}//arr要拦截的工具,handle定义拦截行为var proxy = new Proxy(arr,handle)proxy.push(4) //可以翻到掌握台测试一下会打印出什么
发布者-订阅者模式(backbone.js):
上述先容了大略的一对一双向绑定的实现,即一个数据模型只与一个视图进行绑定。当多个View与一个 Model进行绑定时,每次更新 Model时须要在Model 的set访问器属性中更新多个 View,这样硬编码的办法不利于后期的掩护。为理解决硬编码带来的耦合性过强的问题,在在实际实现中,须要利用到设计模式中的发布 - 订阅模式。
发布 - 订阅模式(又称不雅观察者模式)是一种常用的设计模式,该模式包含发布者和订阅者两种角色。可以让多个订阅者订阅同一个发布者发布的主题,当发布者的主题发生变革时,对外发送一个关照,所有订阅了该主题的订阅者都会吸收到更新的。因此,不雅观察者模式定义的是一种一对多的关系。发布 - 订阅模式非常适宜于 MVVM 双向绑定中多个视图绑定到同一个数据模型的环境。
实现双向数据绑定步骤7要实现mvvm的双向绑定,就必须要实现以下几点:
实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板更换数据,以及绑定相应的更新函数实现一个数据监听器Observer,能够对数据工具的所有属性进行监听,如有变动可拿到最新值并关照订阅者(Dep)实现一个Watcher,Watcher是订阅 - 发布模式中订阅者的实现,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的关照,实行指令绑定的相应回函数 (发布),从而更新视图MVVM入口函数,整合以上三者当新建一个Vue 工具时,框架进入初始化阶段。Vue 在初始化阶段紧张实行两个操作:第一个是遍历系统中数据的所有属性,来对各个属性的变革添加监听;第二个操作是利用指令编译器 Compile对视图中绑定的指令进行扫描进行视图的初始化,然后订阅 Watcher 来更新视图,此时 Watcher 会将自己添加到订阅器Dep中。至此,Vue的初始化过程结束。在系统运行过程中,一旦系统中的数据模型发生了变革,不雅观察者 Observer的 setter 访问器属性就会被触发,此时订阅中央 Dep 会遍历它所掩护的所有订阅者,对付每一个订阅了该数据的工具,向它发出一个更新关照,订阅者收到关照后就会对视图进行相应的更新。以长进程不断往来来往循环,这便是 MVVM 模式在 Vue.js 中的运行事理。
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Two-way data-binding</title></head><body> <div id="app"> <input type="text" v-model="text"> {{ text }} </div> <script> function observe (obj, vm) { Object.keys(obj).forEach(function (key) { defineReactive(vm, key, obj[key]); }); } function defineReactive (obj, key, val) { var dep = new Dep(); Object.defineProperty(obj, key, { get: function () { if (Dep.target) dep.addSub(Dep.target); return val }, set: function (newVal) { if (newVal === val) return val = newVal; dep.notify(); } }); } function nodeToFragment (node, vm) { var flag = document.createDocumentFragment(); var child; while (child = node.firstChild) { compile(child, vm); flag.appendChild(child); } return flag; } function compile (node, vm) { var reg = /\{\{(.)\}\}/; // 节点类型为元素 if (node.nodeType === 1) { var attr = node.attributes; // 解析属性 for (var i = 0; i < attr.length; i++) { if (attr[i].nodeName == 'v-model') { var name = attr[i].nodeValue; // 获取v-model绑定的属性名 node.addEventListener('input', function (e) { // 给相应的data属性赋值,进而触发该属性的set方法 vm[name] = e.target.value; }); node.value = vm[name]; // 将data的值赋给该node node.removeAttribute('v-model'); } } new Watcher(vm, node, name, 'input'); } // 节点类型为text if (node.nodeType === 3) { if (reg.test(node.nodeValue)) { var name = RegExp.$1; // 获取匹配到的字符串 name = name.trim(); new Watcher(vm, node, name, 'text'); } } } function Watcher (vm, node, name, nodeType) { // this为watcher函数 Dep.target = this; // console.log(this); this.name = name; this.node = node; this.vm = vm; this.nodeType = nodeType; this.update(); Dep.target = null; } Watcher.prototype = { update: function () { this.get(); if (this.nodeType == 'text') { this.node.nodeValue = this.value; } if (this.nodeType == 'input') { this.node.value = this.value; } }, // 获取daa中的属性值 get: function () { this.value = this.vm[this.name]; // 触发相应属性的get } } function Dep () { this.subs = [] } Dep.prototype = { addSub: function(sub) { this.subs.push(sub); }, notify: function() { this.subs.forEach(function(sub) { sub.update(); }); } }; function Vue (options) { this.data = options.data; var data = this.data; observe(data, this); var id = options.el; var dom = nodeToFragment(document.getElementById(id), this); // 编译完成后,将dom返回到app中 document.getElementById(id).appendChild(dom); } var vm = new Vue({ el: 'app', data: { text: 'hello world' } }); </script></body></html>
我的理解架构意义角度(Web真个角度):MVC和MVVM在实质上都是为了实现View和Model的解耦,MVC是通过Controller实现了View和Model的解耦,一样平常用与客户端,或者Web真个全体架构过程;而MVVM是在MVC发展到MVP后(为了彻底办理View和Model的耦合问题),在提出前后端分离的根本上(考虑Coltroller的复用性,接口复用性),对View层进行了增强(Vue.js),或者说细化了View层的表现手腕,提出了通过ViewModel对视图层的View和Model解耦。个人觉得MVVM和MVP的整体架构是有相似的地方的,不同的是面对的问题域不同,MVP是Web架构整体的办理方案,MVVM紧张用于构建基于事宜驱动的 UI 平台(界面),适用于前端开拓领域中数据与界面相稠浊的情形,以是它只专注于视图层,抽象出视图的状态和行为,实现了用户界面的UI(View)和数据(Model)的解耦。这个View和Model虽然和MVC中描述的一样,但是不相同的,可以理解为MVC中View中包含了MVVM的架构办法。一样平常前后端分离的Web开拓中会结合MVC和MVVM两种架构模式。利用MVC构建整体的Web架构,利用MVVM办理View层DOM和data的耦合问题。设计模式角度考虑 :MVC是基于不雅观察者设计模式的,Model作为一个主题,View作为不雅观察者,当一个Model变革时,会关照更新一个或多个依赖的View,反之;MVVM可以看做是基于中介者设计模式和不雅观察者设计模式,View和Model通过ViewModel这个中介者工具进行交互,解耦了View和Model的同时实现数据双向绑定。同时ViewModel 作为一个主题工具,View和Model为两个不雅观察者(或者可以理解为View为主题时,Model为不雅观察者,反之。这里的Model View起到一个注册,关照的浸染,对付不雅观察者模式的定义,ModelView是主题的行为,但实际变革的是View或者Model,个人以为两种理解都没问题,理解不对的请小伙伴指出来),当Model变革时,ViewModel由数据绑定关照并更新与之干系的多个View,反之,当View变革时,ViewModel由DOM监听关照更新干系的多个Model。引用文献资料浅析 web 前端 MVVM[db/ol].https://zhuanlan.zhihu.com/p/54355504 ↩︎百度百科[db/ol].https://baike.baidu.com/item/MVVM/96310?fr=aladdin ↩︎上海科创数据资源中央[db/ol].http://www.sstir.cn/search/list?keyword=MVVM ↩︎ ↩︎程桂花.MVVM前后端数据交互中安全机制的研究与实现[D].浙江理工大学硕士学位设计,2017:6-7 ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎你真的理解了MVC, MVP, MVVM吗?[db/ol].https://blog.csdn.net/wdr2003/article/details/79811767 ↩︎易剑波.基于 MVVM 模式的 WEB 前端框架的研究[D].打算机工程运用技能,2016.19:76] ↩︎Vue MVVM理解及事理实现[db/ol].https://juejin.cn/post/6844903929298288647 ↩︎ ↩︎