实现MVC的目的就是为了让M和V相分离。前端的MVC无法做到View和Model的相分离,而MVVM可以。

我们先来看一个用MVC模式开发的经典例子:(一定要深入了解这种开发的思想,而不是看懂代码)

$(function(){
//基本的Todo模型,
var Todo = Backbone.Model.extend({
// 设置模型的默认属性
defaults: {
content: "empty todo...",
done: false
},
//确保每一个模型的content都不为空
initialize: function() {
if (!this.get("content")) {
this.set({"content": this.defaults.content});
}
},
// 将模型的done属性值置为反,比如,之前的done如果为true,那么调用toggle方法后,就会被置为false
toggle: function() {
this.save({done: !this.get("done")});
},
//删除模型中的数据
clear: function() {
this.destroy();
}
});
//Todo的一个集合,数据通过localStorage存储在本地。
var TodoList = Backbone.Collection.extend({
// 设置Collection的模型为Todo
model: Todo,
//存储到本地,在todos-backbone的命名空间中
localStorage: new Store("todos-backbone"),
done: function() {
return this.filter(function(todo){ return todo.get('done'); }); //如果模型的done属性为true,就返回此模型
},
nextOrder: function() { //得到当前集合中最后一个模型的order属性值,然后加1.
if (!this.length) return 1;
return this.last().get('order') + 1;
},
//Backbone内置函数,根据todo对象的order属性值进行排列
comparator: function(todo) {
return todo.get('order');
}
});
// 创建一个全局的Todos的collection对象
var Todos = new TodoList; var TodoView = Backbone.View.extend({
//下面这个标签的作用是,把template模板中获取到的html代码放到这标签中。
tagName: "li",
// 一个Todo模型显示在页面上的模板
template: _.template($('#item-template').html()), // 为每一个Todo模型在页面上的视图绑定事件
events: {
"click .check" : "toggleDone", //点击每一个todo模型的视图时,就会改变此模型done属性值

    "dblclick label.todo-content" : "edit",   //双击此视图时,就可以编辑里面的内容
    "click span.todo-destroy" : "clear",

      "keypress .todo-input"      : "updateOnEnter",    //在视图的内容中按下按键,就会执行undateOnEnter方法
"blur .todo-input" : "close" //可以通过按下enter按键保存,也可以通过鼠标失去焦点保存
}, //初始化设置了TodoView和Todo的一对一引用,这里我们可以把todoview看作是todo在页面上的映射。
initialize: function() {
_.bindAll(this, 'render', 'close', 'remove');
this.model.bind('change', this.render); //model只要一改变,就会马上在视图上显示
},
// 渲染todo模型中的数据到 item-template 中,把生成的html添加到view的el元素中。
render: function() {
$(this.el).html(this.template(this.model.toJSON()));return this;
},
// 改变模型的done属性值
toggleDone: function() {
this.model.toggle();
},

    edit: function() {
      $(this.el).addClass("editing");
      this.input.focus();
    },

// 关闭编辑界面,并把修改内容保存到后台,这里会保存在localStorage中
close: function() {
this.model.save({content: this.input.val()});
$(this.el).removeClass("editing");
},
// 按下回车之后,才关闭编辑界面
updateOnEnter: function(e) {
if (e.keyCode == 13) this.close();
},
// 移除对应条目,以及对应的数据对象
clear: function() {
this.model.clear();
}
}
var AppView = Backbone.View.extend({
//绑定页面上主要的DOM节点
el: $("#todoapp"),
// 在页面底部显示的统计数据模板
statsTemplate: _.template($('#stats-template').html()),
// 绑定dom节点上的事件
events: {
"keypress #new-todo": "createOnEnter", //按下enter键,就会新建一个todo模型,显示在页面中"click .todo-clear a": "clearCompleted", //删除模型的done为true的view
"click .mark-all-done": "toggleAllComplete" //如果点击全部完成的元素,那么就把所有的model的done设置为此元素的状态,此元素选上,就设置为true,不选上,就设置为false
},
//在初始化过程中,绑定事件到Todos上,当任务列表改变时会触发对应的事件。最后把存在localStorage中的数据取出来。
initialize: function() {
//下面这个是underscore库中的方法,用来绑定方法到目前的这个对象中,是为了在以后运行环境中调用这些方法的时候是在这个this上下文执行。
_.bindAll(this, 'addOne', 'addAll', 'render', 'toggleAllComplete'); this.input = this.$("#new-todo");
this.allCheckbox = this.$(".mark-all-done")[0]; Todos.bind('add', this.addOne); //给集合绑定事件
Todos.bind('reset', this.addAll); //取到数据后,就会执行addAll方法
Todos.bind('all', this.render); //执行完addAll方法后,就会执行render方法,因为,集合取到数据时,会触发reset方法和all方法,但是reset方法会先执行。all事件的意思就是,只要Todos有改变,就会触发此事件 Todos.fetch(); //从服务器请求数据,由于此集合有localStorage属性,就说明,它是从本地的取数据
},
render: function() {
var done = Todos.done().length; //从本地取到的todo模型中,有多少个的done属性是true。
this.$('#todo-stats').html(this.statsTemplate({
total: Todos.length,
done: done
}));
//如果全部完成了,就把页面上的checkbox勾上,如果没有,就不用勾上
this.allCheckbox.checked = (done===Todos.length);
},
addOne: function(todo) {
var view = new TodoView({model: todo}); //针对每一个todo模型,我们新建一个todoView视图,这样就实现了一个模型todo对应一个视图tudoview。
this.$("#todo-list").append(view.render().el); //并把每一个模型对应的视图添加到页面中
},
addAll: function() {
Todos.each(this.addOne); // 对集合中的每一个模型todo,调用addOne方法。
},
//生成一个新Todo的所有属性的字典
newAttributes: function() {
return {
content: this.input.val(),
order: Todos.nextOrder(),
done: false
};
},
createOnEnter: function(e) {
if (e.keyCode != 13) return;
Todos.create(this.newAttributes()); //在集合中创建一个todo模型之后会添加到集合中,这时会触发集合的add事件。在视图appview就会新建这个model的view显示在页面上
this.input.val('');
},
// 去掉model的done属性值为true的view
clearCompleted: function() {
_.each(Todos.done(), function(todo){ todo.clear(); });
return false;
},//处理页面点击标记全部完成按钮
//处理逻辑:如果标记全部按钮已选,则所有都完成,如果未选,则所有的都未完成。
toggleAllComplete: function () {
var done = this.allCheckbox.checked;
Todos.each(function (todo) { todo.save({'done': done}); });
} });
var App = new AppView; //新建一个视图
});

上面的这个例子中,总共定义了一个模型Todo,一个集合TodoList,一个视图TodoView,一个视图AppView,没有定义Router路由。

这里面AppView是一个父元素,它的每一个子元素就是一个TodeView。

TodoList集合中的每一个模型就是Todo。

AppView操作TodoList集合。TodeView操作Todo。

在TodeView中会给Todo模型绑定change事件,也就是说,只要Todo模型改变了,相对应的视图就会执行render,把改变的model呈现出来。

在AppView中会给TodoList集合绑定all事件,只要集合TodoList有变化,就会调用AppView视图中的render方法,把改变的TodoList呈现出来。

从Backbone的例子中可以看出,MVC中的V不仅仅只是显示数据,还包括绑定事件,调用模型的方法等等。

接下来,我们来看看使用MVVM实现的一个例子:Angular实现的

<html ng-app='TestFormModule'>  //添加ng-app指令到<html>标签中,这个指令会告诉Angular处理整个HTML页面,也就是说ng-app指令标记了AngularJS脚本的作用域,开发者也可以在局部使用ng-app指令,如<div ng-app>,则AngularJS脚本仅在该<div>中运行。
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script src="../angular-1.0.3/angular.min.js"></script> //引入angular库
</head>
<body>
<form name="myForm" ng-submit="save()" ng-controller="TestFormController"> //ng-controller,一个指令,用于指定当前的模版对应的Controller为TestFormModule。ng-submit,一个指令,form表提交时需要做的事情,这里是调用save方法。
<input name="userName" type="text" ng-model="user.userName" required/> //ng-model,一个指令,用于指定input的值对应modeluser的userName变量。也就是说把此元素input绑定到一个叫user.userName的模型变量中
<input name="password" type="password" ng-model="user.password" required/> //把此元素input绑定到一个叫user.password的模型变量中
<input type="submit" ng-disabled="myForm.$invalid"/>
</form>
</body>
<script>
  var appModule = angular.module('TestFormModule', []); //定义TestFormModule模块
  appModule.controller("TestFormController",function($scope){ //这个控制器的作用域$scope对所有<form ng-controller="TestFormModule">标签内的数据绑定有效。一个作用域可以视作视图、模型和控制器协同工作的粘接器。AngularJS使用作用域,这可以帮助模型和视图分离,但是他们两者却是同步的!任何对于模型的更改都会即时反映在视图上;任何在视图上的更改都会被立刻体现在模型中。
$scope.user={ //初始化模型user
userName:'chaojidan',
password:''
};
$scope.save=function(){ //声明sava方法
alert("保存数据!");
}
}); </script>
</html>

上面这个例子的效果就是:有一个form表,form表中有两个input输入框和一个input按钮,第一个输入框的内容是chaojidan,第二个输入框的内容为"",input按钮是提交按钮。

当两个输入框中都有内容时,按钮才可点,当你点击按钮,页面上才会"弹出保存数据!",如果有一个没有内容,按钮是处于disable状态的,提交不了的。

在input输入框的元素上,有一个ng-model属性,这个属性的值对应$scope.user对象中的userName和password,我们可以暂且称$scope.user为model。也就是说,页面上的input输入框元素(我们称之为view),与model进行了绑定。

同时,input按钮会绑定form表的验证事件,如果form表中的input输入框全部验证通过,就会把input按钮变为可点击,可提交。如果有一个验证不通过,就会让元素input按钮,变成不可点击,不可提交。

angular封装了所有的事件绑定,这里的view层,就是页面的元素。通过给元素上面添加专有的属性,来控制此元素。上面的这个例子,做到了View和Model的完全分离。

最后,大家应该还记得在使用backbone开发时,当model有变化时,就会把整个model的数据拼接到模板中,然后显示出来,但是在angular开发中,我们只需要绑定model和view就行了,至于它是如何显示到页面上,都在angular中封装了,我们不用写代码来实现。

但是,我们需要按照angular规定的语法,来写html和js,甚是不自由。

加油!

第四十六课:MVC和MVVM的开发区别的更多相关文章

  1. NeHe OpenGL教程 第四十六课:全屏反走样

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  2. 潭州课堂25班:Ph201805201 django 项目 第四十六课 查错 补缺 (课堂笔记

    从讲项目开始,查找错误,完善笔记,尽可能 翻译没一句代码(以后台为主), 本项目亮点,也是重点 Django ORM中对数据查询的优化(only.defer.select_related) redis ...

  3. python第四十六课——函数重写

    3.函数重写(override) 前提:必须有继承性 原因: 父类中的功能(函数),子类需要用,但是父类中函数的函数体内容和我现在要执行的逻辑还不相符 那么可以将函数名保留(功能还是此功能),但是将函 ...

  4. JAVA学习第四十六课 — 其它对象API(二)Date类 &amp; Calendar类(重点掌握)

    Date类(重点) 开发时,会时常遇见时间显示的情况,所以必须熟练Date的应用 <span style="font-family:KaiTi_GB2312;font-size:18p ...

  5. NeHe OpenGL教程 第三十六课:从渲染到纹理

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  6. vue.js 第十课-第十六课

    第十课: http://note.youdao.com/noteshare?id=25b5ba45286464856f21eb4b6b391ecd&sub=19C4429995384F72BD ...

  7. NeHe OpenGL教程 第四十八课:轨迹球

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  8. NeHe OpenGL教程 第四十五课:顶点缓存

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  9. Kali Linux Web 渗透测试视频教程— 第十六课-拒绝服务攻击

    Kali Linux Web 渗透测试视频教程— 第十六课-拒绝服务攻击 文/玄魂 目录 Kali Linux Web 渗透测试视频教程— 第十六课-拒绝服务攻击................... ...

随机推荐

  1. Python pass语句作用与用法

    Python中的pass语句作用是什么?表示它不做任何事情,一般用做占位语句.pass语句具体作用及使用方法,我们往下看. pass语句在函数中的作用 当你在编写一个程序时,执行语句部分思路还没有完成 ...

  2. WEB安全--CSRF防御

    CSRF漏洞防御主要可以从三个层面进行,即服务端的防御.用户端的防御和安全设备的防御. 服务端的防御 目前服务器端防御CSRF攻击主要有5种策略(我知道的就这么多):验证HTTP Referer字段, ...

  3. 【ASP.NET 基础】Page类和回调技术

    Page 类有一个 IsPostBack 属性,这个属性用来指示当前页面是第一次加载还是响应了页面上某个控件的服务器事件导致回发而加载. 1.asp.net页面的声明周期 asp.net页面运行的时候 ...

  4. 软件工程实训项目案例--Android移动应用开发

    实训过程 角色分工 1.项目经理:负责项目的组织实施,制定项目计划,并进行跟踪管理 2.开发人员:对项目经理及项目负责 3.需求分析员:负责系统的需求获取和分析,并协助设计人员进行系统设计 4.系统设 ...

  5. UVA-10269 (floyd+dijkstra)

    题意: 现在有A个村庄,B个城堡,现在要从1到A+B,有M条路,魔法鞋最多能用K次,每次的长度不超过L,且起点和终点一定是村庄和城堡,而且每次使用魔法鞋不能穿过城堡,问最短时间是多少; 思路: 先用F ...

  6. 2014 Super Training #6 H Edward's Cola Plan --排序+二分

    原题: ZOJ 3676  http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3676 题意:给每个朋友一瓶可乐,可乐有普通和高 ...

  7. HDU 3400 Line belt【三分套三分】

    从A出发到D,必定有从AB某个点E出发,从某个点F进入CD 故有E,F两个不确定的值. 在AB上行走的时间   f = AE / p 在其他区域行走的时间 g = EF / r 在CD上行走的时间   ...

  8. HOJ 2678 Stars

    题意:N个星星(x,y,z),星星的等级等于x,y,z都小于等于它的星星数量,问每个等级有多少星星. 思路:最暴力的方法是三维树状数组.但是会超内存.所以我们对其中一维先排好序,然后用二维的做. 代码 ...

  9. sql 入门经典(第五版) Ryan Stephens 学习笔记 (第一,二,三,,四,五章)

    SQL - Structured  Query Language (结构化查询语言) 1/ SQL 命令的类型 : 数据定义语言: DDL 数据操作语言: DML 数据查询语言: DQL 数据控制语言 ...

  10. IUYYLIUIU

    #include<cstdio> using namespace std; int main() { printf("IIIIU !"); ; } //http://c ...