MVVM MVC
MVVM是MVC的增强版,实质上和MVC没有本质区别,只是代码的位置变动而已.
MVC
![](https://img2020.cnblogs.com/blog/990019/202110/990019-20211003094710053-636410639.png)
Model:数据模型,用来存储数据
View:视图界面,用来展示UI界面和响应用户交互
Controller:控制器(大管家角色),监听模型数据的改变和控制视图行为、处理用户交互
他们工作和关系看起来是如此清晰,是一种非常好的设计思想。
MVVM
![](https://img2020.cnblogs.com/blog/990019/202110/990019-20211003095203780-1570352134.png)
MVVM是Model-View-ViewModel的简写。它本质上就是MVC (Model-View- Controller)的改进版。即模型-视图-视图模型。【模型】指的是后端传递的数据。【视图】指的是所看到的页面。【视图模型】mvvm模式的核心,它是连接view和model的桥梁。它有两个方向:一是将【模型】转化成【视图】,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。二是将【视图】转化成【模型】,即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听。这两个方向都实现的,我们称之为数据的双向绑定。
总结:在MVVM的框架下视图和模型是不能直接通信的。它们通过ViewModel来通信,ViewModel通常要实现一个observer观察者,当数据发生变化,ViewModel能够监听到数据的这种变化,然后通知到对应的视图做自动更新,而当用户操作视图,ViewModel也能监听到视图的变化,然后通知数据做改动,这实际上就实现了数据的双向绑定。并且MVVM中的View 和 ViewModel可以互相通信。
MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。当然这些事 ViewModel 已经帮我们做了,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。MVVM(Model-View-ViewModel)框架的由来便是MVP(Model-View-Presenter)模式与WPF结合的应用方式时发展演变过来的一种新型架构框架。它立足于原有MVP框架并且把WPF的新特性糅合进去,以应对客户日益复杂的需求变化。
为什么会有MVVM框架?
在过去的10年中,我们已经把很多传统的服务端代码放到了浏览器中,这样就产生了成千上万行的javascript代码,它们连接了各式各样的HTML 和CSS文件,但缺乏正规的组织形式,这也就是为什么越来越多的开发者使用javascript框架。比如:angular、react、vue。浏览器的兼容性问题已经不再是前端的阻碍。前端的项目越来越大,项目的可维护性和扩展性、安全性等成了主要问题。当年为了解决浏览器兼容性问题,出现了很多类库,其中最典型的就是jquery。但是这类库没有实现对业务逻辑的分成,所以维护性和扩展性极差。综上两方面原因,才有了MVVM模式一类框架的出现。比如vue,通过数据的双向绑定,极大了提高了开发效率。
1. 双向绑定
号称是最难理解的地方,
标准的数据驱动开发,应该如上图所示,在一个View的生命周期内,一个ViewModule会管理一个DomainObject(业务模型),一个DO可能包括多个Module数据模型,一个Module可能来自多个数据源,而不是想很多所谓的MVVM框架那样强迫一个M来一个数据源
按照上图标准分层方式来划分的好处,在于,逻辑清晰,Module层粒度够细,可以被多次复用
DO层与VM层View层属于一一对应关系,方便对数据做增删改查的同步
每一层应该是独立的,非一定要使用MVVM框架的紧耦合,可以用自己使用不同的js插件或者模块实现MVVM
我们抛弃框架,单纯的看数据,其实我们要解决的问题很简单
a) 当DO对象属性放生变化时候,通知View更新
b) 当View上表单值放生变化时,通知DO更新,并异步通知队列同步到数据源
先来看问题a,这个最简单,DO是一个基本的Javascript Object,我们在View上的模板显示是这个Object.property,
改变一个Object对象的方式无非几种,一种是
a) 显示Object.property = ‘你好’
b) xxxx.methodName(Object, ‘property’, ‘你好’)
c) xxxx.merge(Object, {‘property’: ‘你好’})
如果是a的情况,ES5+,可以通过设置Object.defefineProperty(‘property’,{set: functiono(){},get:function(){}}),来做赋值和取值的监控触发
对于IE8一下,因为js不支持运算符重载,所以暂时没有好的办法,所以如果只考虑移动端的话,直接defineProperty就全部搞定,如果是要考虑PC的话,就不建议开发者使用直接赋值的方式,参考java的开发模式,也是推荐OOP时候,使用set方式赋值,而不是直接=赋值。
当然了,如果你非要兼容IE8一下的话,用定时器做轮训,配合for in 反射,通过脏数据与原始备份对比的方法也是一种办法,不过这种办法在当前页面非常耗性能,由于IE8一下不支持多线程,HTML5 worker,如果未来flash 插件支持多线程的话,倒是可以用js和flash插件做线程交互的方式做脏数据检测。
如果是b的情况,那就太简单了,在methodName里面触发对于该属性修改的回调即可,如何注册回调呢,首先我们要实现一个类似Dom Event的自定义对象的Event模型,然后通过类似Dom Event的注册事件方式,注册观察者,订阅事件,当执行了methodName时候,发送消息,通知所有订阅者执行回调
如果是c的情况,类似b一样处理
这样一看,双向数据绑定的问题就非常简单的解决了。
我们再来看另外一个MVVM的问题,非简单数据模型,复合数据模型(DO的属性值不是一个string,而是一个Object,且这个Object可能还嵌套多层Obejct的时候)的处理办法,这个一般的MVVM框架直接不考虑,或者通过长字段名的方式绕过这个问题
这个问题是这样的,早在10几年前,java structs框架流行的时候就出现了,当一个表单,出现需要对两个Java Bean做update操作时候,一个bean是user,一个bean是成绩
对应的表单字段名,就是 user表.name,user表.id,score表.point,
在struct2里面,处理逻辑是把 “点”作为特殊符号,在做form序列化时候,非包含点的字段的值都是string,包含点的字段是一个Object,比如刚才的form序列化之后结果就是 { user: {id :’’ , name: ‘’}, score: {id: ‘’, point: ‘’}}
同理在MVVM实现时,也是一样,认为点是分割对象的关键字,这样我们就可以实现把多个对象嵌套到View模板里面,实现复合Object的双向映射。
当然,说了这么多全是理论的东西,没有任何代码和演示,你可能都困了,至于代码我就不列举了,GitHub上有很多案例,这里推荐两个C-41和MVVMTest
=============================================
Model 层
Model 层,对应数据层的域模型,它主要做域模型的同步
。通过 Ajax/fetch 等 API 完成客户端和服务端业务 Model 的同步。在层间关系里,它主要用于抽象出 ViewModel 中视图的 Model。
View 层
View 层,作为视图模板存在,在 MVVM 里,整个 View 是一个动态模板。除了定义结构、布局外,它展示的是 ViewModel 层的数据和状态。View 层不负责处理状态,View 层做的是 数据绑定的声明、 指令的声明、 事件绑定的声明。
ViewModel 层
ViewModel 层把 View 需要的层数据暴露,并对 View 层的 数据绑定声明、 指令声明、 事件绑定声明 负责,也就是处理 View 层的具体业务逻辑。ViewModel 底层会做好绑定属性的监听。当 ViewModel 中数据变化,View 层会得到更新;而当 View 中声明了数据的双向绑定(通常是表单元素),框架也会监听 View 层(表单)值的变化。一旦值变化,View 层绑定的 ViewModel 中的数据也会得到自动更新。
前端 MVVM 图示
如图所示,在前端 MVVM 框架中,往往没有清晰、独立的 Model 层。在实际业务开发中,我们通常按 Web Component 规范来组件化的开发应用,Model 层的域模型往往分散在在一个或几个 Component 的 ViewModel 层,而 ViewModel 层也会引入一些 View 层相关的中间状态,目的就是为了更好的为 View 层服务。
开发者在 View 层的视图模板中声明 数据绑定、 事件绑定 后,在 ViewModel 中进行业务逻辑的 数据 处理。事件触发后,ViewModel 中 数据 变更, View 层自动更新。因为 MVVM 框架的引入,开发者只需关注业务逻辑、完成数据抽象、聚焦数据,MVVM 的视图引擎会帮你搞定 View。因为数据驱动,一切变得更加简单。
MVVM框架的工作
不可置否,MVVM 框架极大的提升了应用的开发效率。It's amazing!But,MVVM 框架到底做了什么?
-
视图引擎
视图引擎:我是视图引擎,我为 View 层作为视图模板提供强力支持,开发者,你们不需要操作 DOM ,丢给我来做!
-
数据存取器
数据存取器:我是数据存取器,我可以通过 Object.defineProperty()
API 轻松定义,或通过自行封装存取函数的方式曲线完成。我的内部往往封装了 发布/订阅模式,以此来完成对数据的监听、数据变更时通知更新。我是 数据绑定 实现的基础。
-
组件机制
组件机制:我是组件机制。有追求的开发者往往希望按照面向未来的组件标准 - Web Components 的方式开发,我是为了满足你的追求而生。MVVM 框架提供组件的定义、继承、生命周期、组件间通信机制,为开发者面向未来开发点亮明灯。
-
more...
结语
有了前端 MVVM 框架,应用开发如此简单!
前端 MVVM 已是趋势,是大型 Web 应用开发效率提升的利器。由百度 EFE 出品的 MVVM 框架 - san,在保持功能强大、特性支持完整的前提下,还兼顾到IE8的市场份额,对老版本浏览器提供了良好的兼容性,更难能可贵的是 GZip 后体积仅 11k, 现已为百度内多个产品提供了强劲驱动,可谓百度 EFE 又一精工之作!开源的 san 欢迎广大开发者体验、使用,更欢迎广大开发者加入到 san 生态 的建设中来,让 san 变得更好!