在看 mvc 和 mvvm 的区别之前咱们来看一下前端的倒退历史
在上个世纪的 1989 年,欧洲核子研究中心的物理学家 Tim Berners-Lee 创造了超文本标记语言(HyperText Markup Language),简称 HTML,并在 1993 年成为互联网草案。从此,互联网开始迅速商业化,诞生了一大批商业网站。
最早的 HTML 页面是齐全动态的网页,它们是事后编写好的寄存在 Web 服务器上的 html 文件。浏览器申请某个 URL 时,Web 服务器把对应的 html 文件扔给浏览器,就能够显示 html 文件的内容了。
如果要针对不同的用户显示不同的页面,显然不可能给成千上万的用户筹备好成千上万的不同的 html 文件,所以,服务器就须要针对不同的用户,动静生成不同的 html 文件。一个最间接的想法就是利用 C、C++ 这些编程语言,间接向浏览器输入拼接后的字符串。这种技术被称为 CGI:Common Gateway Interface。
很显然,像新浪首页这样的简单的 HTML 是不可能通过拼字符串失去的。于是,人们又发现,其实拼字符串的时候,大多数字符串都是 HTML 片段,是不变的,变动的只有多数和用户相干的数据,所以,又呈现了新的创立动静 HTML 的形式:ASP、JSP 和 PHP——别离由微软、SUN 和开源社区开发。
在 ASP 中,一个 asp 文件就是一个 HTML,然而,须要替换的变量用非凡的 <%=var%> 标记进去了,再配合循环、条件判断,创立动静 HTML 就比 CGI 要容易得多。
然而,一旦浏览器显示了一个 HTML 页面,要更新页面内容,惟一的办法就是从新向服务器获取一份新的 HTML 内容。如果浏览器想要本人批改 HTML 页面的内容,就须要等到 1995 年年底,JavaScript 被引入到浏览器。
有了 JavaScript 后,浏览器就能够运行 JavaScript,而后,对页面进行一些批改。JavaScript 还能够通过批改 HTML 的 DOM 构造和 CSS 来实现一些动画成果,而这些性能没法通过服务器实现,必须在浏览器实现。
用 JavaScript 在浏览器中操作 HTML,经验了若干倒退阶段:
第一阶段,间接用 JavaScript 操作 DOM 节点,应用浏览器提供的原生 API:
第二阶段,因为原生 API 不好用,还要思考浏览器兼容性,jQuery 横空出世,以简洁的 API 迅速俘获了前端开发者的芳心:
第三阶段,MVC 模式,须要服务器端配合,JavaScript 能够在前端批改服务器渲染后的数据。
当初,随着前端页面越来越简单,用户对于交互性要求也越来越高,想要写出 Gmail 这样的页面,仅仅用 jQuery 是远远不够的。MVVM 模型应运而生。
MVVM 最早由微软提出来,它借鉴了桌面应用程序的 MVC 思维,在前端页面中,把 Model 用纯 JavaScript 对象示意,View 负责显示,两者做到了最大限度的拆散。
2. 当初让咱们从 MVC 开始
简直所有的 App 都只干这么一件事:将数据展现给用户看,并解决用户对界面的操作。
MVC 的思维:一句话形容就是 Controller 负责将 Model 的数据用 View 显示进去,换句话说就是在 Controller 外面把 Model 的数据赋值给 View,比方在 controller 中写 document.getElementById(“box”).innerHTML = data[”title”],只是还没有刻意建一个 Model 类进去而已。
M、V、C
Model(模型):是应用程序中用于解决应用程序数据逻辑的局部。
通常模型对象负责在数据库中存取数据。
比方咱们人类有一双手,一双眼睛,一个脑袋,没有尾巴,这就是模型,Model 定义了这个模块的数据模型。在代码中体现为数据管理者,Model 负责对数据进行获取及寄存。数据不可能凭空生成的,要么是从服务器下面获取到的数据,要么是本地数据库中的数据,也有可能是用户在 UI 上填写的表单行将上传到服务器下面寄存,所以须要有数据起源。既然 Model 是数据管理者,则天然由它来负责获取数据。Controller 不须要关怀 Model 是如何拿到数据的,只管调用就行了。数据寄存的中央是在 Model,而应用数据的中央是在 Controller,所以 Model 应该提供接口供 controller 拜访其寄存的数据(通常通过.h 外面的只读属性)
View(视图):是应用程序中解决数据显示的局部。
通常视图是根据模型数据创立的。
View,视图,简略来说,就是咱们在界面上看见的所有。它们有一部分是咱们 UI 定死的,也就是不会依据数据来更新显示的,比方一些 Logo 图片啊,这里有个按钮啊,那里有个输入框啊,一些显示特定内容的 label 啊等等;有一部分是会依据数据来显示内容的,比方 tableView 来显示好友列表啊,这个 tableView 的显示内容必定是依据数据来显示的。咱们应用 MVC 解决问题的时候,通常是解决这些依据数据来显示内容的视图。
Controller(控制器):是应用程序中解决用户交互的局部。
通常控制器负责从视图读取数据,管制用户输出,并向模型发送数据。
Controller 是 MVC 中的数据和视图的协调者,也就是在 Controller 外面把 Model 的数据赋值给 View 来显示(或者是 View 接管用户输出的数据而后由 Controller 把这些数据传给 Model 来保留到本地或者上传到
服务器)。
综合以上内容,实际上你应该能够通过面向对象的根本思维来推导出 controller 呈现的起因:咱们所有的 App 都是界面和数据的交互,所以须要类来进行界面的绘制,于是呈现了 View,须要类来治理数据于是呈现了 Model。咱们设计的 View 应该能显示任意的内容比方页面中显示的文字应该是任意的而不只是某个特定 Model 的内容,所以咱们不应该在 View 的实现中去写和 Model 相干的任何代码,如果这样做了,那么 View 的可扩展性就相当低了。而 Model 只是负责解决数据的,它基本不晓得数据到时候会拿去干啥,可能拿去作为算法噼里啪啦去了,可能拿去显示给用户了,它既然无奈接管用户的交互,它就不应该去管和视图相干的任何信息,所以 Model 中不应该写任何 View 相干代码。然而咱们的数据和界面应该同步,也就是肯定要有个中央要把 Model 的数据赋值给 View,而 Model 外部和 View 的外部都不可能去写这样的代码,所以只能新发明一个类进去了,取名为 Controller。
3. 上面来看这张图
斯坦福大学公开课上的这幅图来阐明,这能够说是最经典和最标准的 MVC 规范
mvc.png
这张图把 MVC 分为三个独立的区域,并且两头用了一些线来隔开。很有意思的设计,因为这些线仿佛呈现在了驾校科目一的内容中,你瞧 C 和 V 以及 C 和 M 之间的白线,一部分是虚线一部分是实线对吧,这就表明了援用关系:C 能够间接援用 V 和 M,而 V 和 M 不能间接援用 C,至多你不能显式的在 V 和 M 的代码中去写和 C 相干的任何代码,而 V 和 M 之间则是双黄线,没错,它们俩谁也不能引用谁,你既不能在 M 外面写 V,也不能在 V 外面写 M。哦,下面的形容有点小小的问题,你不是“不能”这样写,而是“不应该”这样写,没人能阻止你在写代码的时候在一个 M 外面去写 V,然而一旦你这样做了,那么你就违反了 MVC 的标准,你就不是在应用 MVC 了,所以这算是 MVC 的一个必要条件:应用 MVC –> M 外面没有 V 的代码。所以 M 外面没有 V 的代码就是应用 MVC 的必要条件。
View 和 Controller 的交互
按钮点击事件,是 View 来接管的,然而解决这个事件的应该是 Controller,所以 View 把这个事件传递给了 Controller,如何传递的呢,见图,看到 View 下面的 action 没有,这就是事件,看到 Controller 下面的 target 没有,这就是靶子,View 到底要把事件传递给谁,它被规定了传递给靶子,Controller 实际上就是靶子。只是 View 只负责传递事件,不负责关怀靶子是谁。就像你是一个负责运货的少年,你惟一晓得的是你要把货(action)交给上头(开发者)通知你的那个收货的人(target),至于那个收货的人是警察还是怪兽,你都不须要关怀。这是 V 和 C 的一种交互方式,叫做 target-action。所以你看,这张图几乎就是神来之笔,旁边还栩栩如生的画出了 V 对 C 的另一种传值:协定 - 委托。委托有两种:代理和数据源。什么是代理,就是专门解决 should、will、did 事件的委托,什么是数据源,就是专门解决 data、count 等等的委托。
Model 和 Controller 的交互
M 是干嘛的?下面说了,M 就是数据管理者,你能够了解为它间接和数据库打交道。这里的数据库可能是本地的,也可能是服务器上的,M 会从数据库获取数据,也可能把数据上传给数据库。M 也将提供属性或者接口来供 C 拜访其持有的数据。咱们就拿一个简略的需要作为例子,如果我想在一个模块中显示一段文字,这段文字是从网上获取下来的。
那么应用 MVC 的话,在 C 中必定须要一个 UILabel(V)作为属性来显示这段文字,而这段文字由谁来获取呢,必定是由 M 来获取了。而获取的中央在哪里呢?通常在 C 的生命周期外面,所以往往是在 C 的一个生命周期办法比方 viewDidLoad 外面调用 M 获取数据的办法来获取数据。当初问题来了,M 获取数据的办法是异步的网络申请,网络申请完结后,C 才应该用申请下来的数据从新赋值给 V,当初的问题是,C 如何晓得网络申请完结了?
这里咱们肯定要换一种角度去思考,咱们进一步思考 M 和 V 之间的关系:它们应该是一种同步的关系,也就是,不论任何时刻,只有 M 的值产生扭转,V 的显示就应该产生扭转(显示最新的 M 的内容)。所以咱们能够关注 M 的值扭转,而不必关怀 M 的网络申请是否完结了。实际上 C 基本不晓得 M 从哪去拿的数据,C 的责任是负责把 M 最新的数据赋值给 V。所以 C 应该关注的事件是:M 的值是否产生了变动。
4.MVVM
MVVM 的诞生
就像咱们之前剖析 MVC 是如何正当调配工作的一样,咱们须要数据所以有了 M,咱们须要界面所以有了 V,而咱们须要找一个中央把 M 赋值给 V 来显示,所以有了 C,然而咱们疏忽了一个很重要的操作:数据解析。在 MVC 出世的年代,手机 APP 的数据往往都比较简单,没有当初那么简单,所以那时的数据解析很可能一步就解决了,所以既然有这样一个问题要解决,而面向对象的思维就是用类和对象来解决问题,显然 V 和 M 早就被定义死了,它们都不应该解决“解析数据”的问题,理所应当的,“解析数据”这个问题就交给 C 来实现了。而当初的手机 App 性能越来越简单,数据结构也越来越简单,所以数据解析也就没那么简略了。如果咱们持续依照 MVC 的设计思路,将数据解析的局部放到了 Controller 外面,那么 Controller 就将变得相当臃肿。还有相当重要的一点:Controller 被设计进去并不是解决数据解析的。1、治理本人的生命周期;2、解决 Controller 之间的跳转;3、实现 Controller 容器。这外面基本没有“数据解析”这一项,所以显然,数据解析也不应该由 Controller 来实现。那么咱们的 MVC 中,M、V、C 都不应该解决数据解析,那么由谁来呢?这个问题实际上在面向对象的时候相当好答复:既然目前没有类可能解决这个问题,那么就创立一个新的类进去解决不就好了?所以咱们聪慧的开发者们就专门为数据解析创立出了一个新的类:ViewModel。这就是 MVVM 的诞生。
如何实现 MVVM
搞清楚了 MVVM 为什么会呈现,将对于你了解如何实现 MVVM 有极大的帮忙。在咱们开始着手实现 MVVM 之前,我先简略提一下之前遗留的一个问题:为什么 MVVM 这个名字外面,没有 Controller 的呈现(为什么不叫 MVCVM,C 去哪了)。原本这个问题应该在实现后再来解释,然而咱们这里是教学,为了让大家更好的明确咱们接下来的思维,所以这里要提前解释一下这个论断:Controller 的存在感被齐全的升高了。咱们在待会实现 MVVM 的时候你就能领会到了,这里请先把这个论断印在脑海当中:Controller 的存在感被齐全的升高了、Controller 的存在感被齐全的升高了、Controller 的存在感被齐全的升高了。
好的,咱们终于要开始着手实现 MVVM 了。如果你曾经搞懂了 MVC,那么用 MVVM 实现一个雷同的性能将会变得非常简单。你只须要记住两点:1、Controller 的存在感被齐全的升高了;2、VM 的呈现就是 Controller 存在感升高的起因。
Controller 存在感升高的起因
在 MVVM 中,Controller 不再像 MVC 那样间接持有 Model 了。设想 Controller 是一个 Boss,数据是一堆文件(Model),如果当初是 MVC,那么数据解析(比方整顿文件)须要由 Boss 亲自实现,然而实际上 Boss 须要的仅仅是整顿好的文件而不是那一堆乌七八糟的整顿前的文件。所以 Boss 招聘了一个秘书,当初 Boss 就不再须要治理原始数据(整顿之前的文件)了,他只须要去找秘书:你帮我把文件整顿好后给我。那么这个秘书就首先去拿到文件(原始数据),而后进行整顿(数据解析),接下来把整顿的后果给 Boss。所以秘书就是 VM 了,并且 Controller(Boss)当初只须要间接持有 VM 而不须要再持有 M 了。如果再进一步了解 C、VM、M 之间的关系:因为 Controller 只须要数据解析的后果而不关怀过程,所以就相当于 VM 把“如何解析 Model”给封装起来了,C 甚至基本就不须要晓得 M 的存在就能把工作做好,前提它须要持有一个 VM。那么咱们 MVVM 中的持有关系就是:C 持有 VM,VM 持有 M。这里有一个比拟争议的中央:C 该不该持有 M。我的答案是不该。为什么呢,因为 C 持有 M 没有任何意义。就算 C 间接拿到了 M 的数据,它还是要去让 VM 进行数据解析,而数据解析就须要 M,那么间接让 VM 持有 M 而 C 间接持有 VM 就足够了。最初再分享一个我在实现 MVVM 中的一个技巧,也谈不上是技巧吧,算是一种必要的思维:一旦在实现 Controller 的过程中遇到任何跟 Model(或者数据)相干的问题,就找 VM 要答案。这个思维待会咱们会在实现代码的时候用到。