mvc
![](http://upload-images.jianshu.io/upload_images/4050018-c7c4ddd5fcc3dfac.png?imageMogr2/auto-orient/strip|imageView2/2/w/601/format/webp)
- View 传送指令到 Controller
- Controller 完成业务逻辑后,要求 Model 改变状态
- Model 将新的数据发送到 View,用户得到反馈
所有通信都是单向的。
mvp
MVP 模式将 Controller 改名为 Presenter,同时改变了通信方向。
![](http://upload-images.jianshu.io/upload_images/4050018-cc553326cc9dc1be.png?imageMogr2/auto-orient/strip|imageView2/2/w/537/format/webp)
- 各部分之间的通信,都是双向的。
- View 与 Model 不发生联系,都通过 Presenter 传递。
- View 非常薄,不部署任何业务逻辑,称为"被动视图"(Passive View),即没有任何主动性,而 Presenter非常厚,所有逻辑都部署在那里。
mvvm
MVVM 模式将 Presenter 改名为 ViewModel,基本上与 MVP 模式完全一致。
![](http://upload-images.jianshu.io/upload_images/4050018-ce27fe8c0d60e299.png?imageMogr2/auto-orient/strip|imageView2/2/w/556/format/webp)
唯一的区别是,它采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然。
这里面使用的设计模式有:观察者模式(发布订阅模式)、代理模式、工厂模式、单例模式。
双向绑定
在 MVVM 中,UI 是通过数据驱动的,数据一旦改变就会相应的刷新对应的 UI,UI 如果改变,也会改变对应的数据。这种方式就可以在业务处理中只关心数据的流转,而无需直接和页面打交道。ViewModel 只关心数据和业务的处理,不关心 View 如何处理数据,在这种情况下,View 和 Model 都可以独立出来,任何一方改变了也不一定需要改变另一方,并且可以将一些可复用的逻辑放在一个 ViewModel 中,让多个 View 复用这个 ViewModel。
三大框架的设计思想
React的单向数据流
React使用的是MVC框架。所有MVC框架都是单向数据流的。
特色
- 使用 Virtual DOM
- 提供了响应式 (Reactive) 和组件化 (Composable) 的视图组件。
- 将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库。
Angluar 的脏数据检测
Angular使用的MVVM的思想,当触发UI事件,ajax请求或者 timeout 延迟,会触发脏检查。这时会调用 $digest
循环遍历所有的listener里的数据,判断当前值是否和先前的值有区别,如果检测到变化的话,会调用$watch
函数,最后把所有的变化全部更新,调用apply()方法把新的数据渲染到页面上。
优点:一次检测会收集所有的数据变化,然后统一更新 UI,大大减少了操作 DOM 的次数。
缺点:只要有ui操作,ajax,settimeout就会进行检查,且当watcher之间相互影响的时候,更会触发多次$digest
循环,这样watcher一多,就会很影响性能。
Vue的数据劫持
Vue一定意义上算是React和Angular的集大成者。它吸取了MVVM的数据管理思想,同时应用了React的virtual Dom算法。它使用了双向数据绑定来满足开发的便捷,但是它不同组件之间又使用单向数据流,来保证数据的可控性。它使用了很多Angular的指令语法,但是他革新了Angular的脏数据检查机制,使用数据劫持的方法来触法数据检查机制。它借鉴了React的组件化思想,大大增加了前端工程的结构规范化。
Vue 内部使用了 Object.defineProperty() 来实现双向绑定,通过这个函数可以监听到 set 和 get 的事件。
使用了Object.defineProperty() 的不足:
- 只能对属性进行数据劫持,所以需要深度遍历,影响性能
- 不能监听数组的数据变化,虽然其实实际上也可以监听数组一些操作的变化,其实本质上是用hack的手段实现的,增、删、换位,都是可以监听的,但是别的就不能了。
反观 Proxy 就没以上的问题,原生支持监听数组变化,并且可以直接对整个对象进行拦截,所以 Vue 也将在下个大版本中使用 Proxy 替换 Object.defineProperty。
单向数据流和双向数据绑定
单向绑定的优点是相应的可以带来单向数据流,这样做的好处是所有状态变化都可以被记录、跟踪,状态变化通过手动调用通知,源头易追溯,没有“暗箱操作”。同时组件数据只有唯一的入口和出口,使得程序更直观更容易理解,有利于应用的可维护性。缺点则是代码量会相应的上升,数据的流转过程变长,从而出现很多类似的样板代码。同时由于对应用状态独立管理的严格要求(单一的全局store),在处理局部状态较多的场景时(如用户输入交互较多的“富表单型”应用),会显得啰嗦及繁琐。
基本上双向绑定的优缺点就是单向绑定的镜像了。优点是在表单交互较多的场景下,会简化大量业务无关的代码。缺点就是由于都是“暗箱操作”,我们无法追踪局部状态的变化(虽然大部分情况下我们并不关心),潜在的行为太多也增加了出错时 debug 的难度。同时由于组件数据变化来源入口变得可能不止一个,新手玩家很容易将数据流转方向弄得紊乱,如果再缺乏一些“管制”手段,最后就很容易因为一处错误操作造成应用雪崩。
这样来看,单向绑定跟双向绑定在功能上基本上是互补的,所以我们可以在合适的场景下使用合适的手段。比如在 UI控件 中(通常是类表单操作),我会使用双向的方式绑定数据;而其他场景则统一采用 单向 + inline event ( <component msg="msg" on-update="updateMsg(msg)"></component> ) 的方式构建应用。
参考链接
http://www.ruanyifeng.com/blog/2015/02/mvcmvp_mvvm.html
https://cn.vuejs.org/v2/guide/comparison.html#React
https://yuchengkai.cn/docs/zh/frontend/framework.html
https://www.zhihu.com/question/49964363
https://www.cnblogs.com/likeFlyingFish/p/6183630.html