理解javascript中的MVC模式


Posted in Javascript onJanuary 28, 2016

MVC模式是软件工程中一种软件架构模式,一般把软件模式分为三部分,模型(Model)+视图(View)+控制器(Controller);

模型:模型用于封装与应用程序的业务逻辑相关的数据以及对数据处理的方法。模型有对数据直接访问的权利。模型不依赖 “视图” 和 “控制器”, 也就是说 模型它不关心页面如何显示及如何被操作.

视图:视图层最主要的是监听模型层上的数据改变,并且实时的更新html页面。当然也包括一些事件的注册或者ajax请求操作(发布事件),都是放在视图层来完成。

控制器:控制器接收用户的操作,最主要是订阅视图层的事件,然后调用模型或视图去完成用户的操作;比如:当页面上触发一个事件,控制器不输出任何东西及对页面做任何处理; 它只是接收请求并决定调用模型中的那个方法去处理请求, 然后再确定调用那个视图中的方法来显示返回的数据。

下面我们来实现一个简单的下拉框控件,我们可以对它进行增删操作;如下图所示:

理解javascript中的MVC模式

代码如下:

/*
 模型用于封装与应用程序的业务逻辑相关的数据以及对数据处理的方法。模型有对数据直接访问的权利。
 模型不依赖 "视图" 和 "控制器", 也就是说 模型它不关心页面如何显示及如何被操作.
*/
function Mode(elems) {
  // 所有元素
  this._elems = elems;
 
  // 被选中元素的索引
  this._selectedIndex = -1;
 
  // 增加一项
  this.itemAdd = new Event(this);
 
  // 删除一项
  this.itemRemoved = new Event(this);
 
  this.selectedIndexChanged = new Event(this);
}
 
Mode.prototype = {
 
  constructor: 'Mode',
 
  // 获取所有的项
  getItems: function(){
    return [].concat(this._elems);
  },
  // 增加一项
  addItem: function(elem) {
    this._elems.push(elem);
    this.itemAdd.notify({elem:elem});
  },
  // 删除一项
  removeItem: function(index) {
    var item = this._elems[index];
    this._elems.splice(index,1);
    this.itemRemoved.notify({elem:item});
 
    if(index === this._selectedIndex) {
      this.setSelectedIndex(-1);
    }
  },
  getSelectedIndex: function(){
    return this._selectedIndex;
  },
  setSelectedIndex: function(index){
    var previousIndex = this._selectedIndex;
    this._selectedIndex = index;
    this.selectedIndexChanged.notify({previous : previousIndex});
  }
};
/*
 下面是观察者模式类,它又叫发布---订阅模式;它定义了对象间的一种一对多的关系,
 让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知。
*/
function Event(observer) {
  this._observer = observer;
  this._listeners = [];
}
Event.prototype = {
  constaructor: 'Event',
  attach : function(listeners) {
    this._listeners.push(listeners);
  },
  notify: function(objs){
    for(var i = 0,ilen = this._listeners.length; i ) {
      this._listeners[i](this._observer,objs);
    }
  }
};
 
/*
 * 视图显示模型数据,并触发UI事件。
 */
function View(model,elements){
  this._model = model;
  this._elements = elements;
 
  this.listModified = new Event(this);
  this.addButtonClicked = new Event(this);
  this.delButtonClicked = new Event(this);
  var that = this;
 
  // 绑定模型监听器
  this._model.itemAdd.attach(function(){
    that.rebuildList();
  });
  this._model.itemRemoved.attach(function(){
    that.rebuildList();
  });
 
  // 将监听器绑定到HTML控件上
  this._elements.list.change(function(e){
    that.listModified.notify({index: e.target.selectedIndex});
  });
  // 添加按钮绑定事件
  this._elements.addButton.click(function(e){
    that.addButtonClicked.notify();
  });
  // 删除按钮绑定事件
  this._elements.delButton.click(function(e){
    that.delButtonClicked.notify();
  });
}
View.prototype = {
  constructor: 'View',
  show: function(){
    this.rebuildList();
  },
  rebuildList: function(){
    var list = this._elements.list,
      items,
      key;
    list.html("");
    items = this._model.getItems();
    for(key in items) {
      if(items.hasOwnProperty(key)) {
        list.append('' +items[key]+ '');
      }
    }
    this._model.setSelectedIndex(-1);
  }
};
/*
 控制器响应用户操作,调用模型上的变化函数
 负责转发请求,对请求进行处理
*/
function Controller(model,view) {
  this._model = model;
  this._view = view;
  var that = this;
 
  this._view.listModified.attach(function(sender,args){
    that.updateSelected(args.index);
  });
  this._view.addButtonClicked.attach(function(){
    that.addItem();
  });
  this._view.delButtonClicked.attach(function(){
    that.delItem();
  });
}
Controller.prototype = {
  constructor: 'Controller',
 
  addItem: function(){
    var item = window.prompt('Add item:', '');
    if (item) {
      this._model.addItem(item);
    }
  },
 
  delItem: function(){
    var index = this._model.getSelectedIndex();
    if(index !== -1) {
      this._model.removeItem(index);
    }
  },
 
  updateSelected: function(index){
    this._model.setSelectedIndex(index);
  }
};

HTML代码如下:

<select id="list" size="10" style="width: 10rem">select>br/>
<button id="plusBtn"> + button>
<button id="minusBtn"> - button>

页面初始化代码如下:

$(function () {
  var model = new Mode(['PHP', 'JavaScript']),
   view = new View(model, {
    'list' : $('#list'), 
    'addButton' : $('#plusBtn'), 
    'delButton' : $('#minusBtn')
    }),
    controller = new Controller(model, view);    
    view.show();
});

代码分析如下:

  先分析下我们是要实现什么样的功能,基本功能有:

一个下拉框,通过用户输入的操作来实现用户增加一项及用户选中一项后删除一项的功能;
当然也添加了用户切换到那一项的事件;

比如我们现在来增加一条数据的时候,在视图层上添加监听事件,如下代码:

// 添加按钮绑定事件
this._elements.addButton.click(function(e){
  that.addButtonClicked.notify();
});

然后调用观察者类Event中的方法notify(发布一个事件) that.addButtonClicked.notify();大家都知道,观察者模式又叫发布-订阅模式,让多个观察者对象同时监听某一个主题对象,当某一个主题对象发生改变的时候,所有依赖它的对象都会得到通知;
因此在控制层(Controller)我们可以使用如下代码对发布者进行监听操作:

this._view.addButtonClicked.attach(function(){
  that.addItem();
});

之后调用自身的方法addItem();代码如下:

addItem: function(){
  var item = window.prompt('Add item:', '');
  if (item) {
    this._model.addItem(item);
  }
}

调用模型层(model)的方法addItem();把一条数据插入到select框里面去;model(模型层)的addItem()方法代码如下:

// 增加一项
addItem: function(elem) {
  this._elems.push(elem);
  this.itemAdd.notify({elem:elem});
},

如上代码 增加一项后,通过 this.itemAdd 发布一个消息,然后在视图层(View)上通过如下代码来监听这个消息;代码如下:

// 绑定模型监听器
this._model.itemAdd.attach(function(){
   that.rebuildList();
});

最后监听到模型上(Model)的数据发生改变后,及时调用自身的方法rebuildList()去更新页面上的数据;

模型层(Model)最主要做业务数据封装操作。视图层(View)主要发布事件操作及监听模型层上的数据,如果模型层上有数据改变的时候,及时更新页面操作,最后显示给页面上来,控制层(Controller)主要监听视图层(View)的事件,调用模型层(Model)的方法来更新模型上的数据,模型层数据更新后,会发布一条消息出去,最后视图层(View)通过监听模型层(Model)的数据变化,来更新页面的显示; 如上是MVC的基本流程。
MVC的优点:
        1. 耦合性低:视图层和业务层分离了,如果页面上显示改变的话,直接在视图层更改即可,不用动模型层和控制层上的代码;也就是视图层 与 模型层和控制层
已经分离了;所以很容易改变应用层的数据层和业务规则。
        2. 可维护性:分离视图层和业务逻辑层也使得WEB应用更易于维护和修改。
MVC的缺点:
        个人觉得适合于大型项目,对于中小型项目并不适合,因为要实现一个简单的增删改操作,只需要一点点JS代码,但是MVC模式代码量明显增加了。
对于学习成本也就提高了,当然如果使用一些封装好的MVC库或者框架就好了。

以上就是关于javascript中的MVC模式实现方法,优缺点的详细分析,希望对大家的学习有所帮助。

Javascript 相关文章推荐
File文件控件,选中文件(图片,flash,视频)即立即预览显示
Apr 09 Javascript
jquery在IE、FF浏览器的差别详细探讨
Apr 28 Javascript
Javascript异步编程模型Promise模式详细介绍
May 08 Javascript
js实现从中间开始往上下展开网页窗口的方法
Mar 02 Javascript
jquery动感漂浮导航菜单代码分享
Apr 15 Javascript
JavaScript动态创建div等元素实例讲解
Jan 06 Javascript
javascript实现延时显示提示框特效代码
Apr 27 Javascript
JS简单实现浮动窗口效果示例
Sep 07 Javascript
AngularJS中指令的四种基本形式实例分析
Nov 22 Javascript
JavaScript中的this陷阱的最全收集并整理(没有之一)
Feb 21 Javascript
vue实现行列转换的一种方法
Aug 06 Javascript
vue 扩展现有组件的操作
Aug 14 Javascript
jQuery获取checkbox选中的值
Jan 28 #Javascript
探讨JavaScript语句的执行过程
Jan 28 #Javascript
Javascript复制实例详解
Jan 28 #Javascript
基于jQuery实现以手风琴方式展开和折叠导航菜单
Jan 28 #Javascript
基于JavaScript的操作系统你听说过吗?
Jan 28 #Javascript
js+canvas绘制矩形的方法
Jan 28 #Javascript
js+canvas简单绘制圆圈的方法
Jan 28 #Javascript
You might like
PHP 批量删除数据的方法分析
2009/10/30 PHP
PHP实现的连贯操作、链式操作实例
2014/07/08 PHP
php查询mysql大量数据造成内存不足的解决方法
2015/03/04 PHP
php和html的区别点详细总结
2019/09/24 PHP
JavaScript中的Window窗口对象
2008/01/16 Javascript
jquery中dom操作和事件的实例学习 仿yahoo邮箱登录框的提示效果
2011/11/30 Javascript
解决jquery版本冲突的有效方法
2014/09/02 Javascript
Javascript表单验证要注意的事项
2014/09/29 Javascript
js判断是否按下了Shift键的方法
2015/01/27 Javascript
jquery让指定的元素闪烁显示的方法
2015/03/17 Javascript
JS/jQuery判断DOM节点是否存在的简单方法
2016/11/24 Javascript
详解微信小程序——自定义圆形进度条
2016/12/29 Javascript
Bootstrap模态对话框中显示动态内容的方法
2018/08/10 Javascript
使用vue根据状态添加列表数据和删除列表数据的实例
2018/09/29 Javascript
在layui中对table中的数据进行判断(0、1)转换为提示信息的方法
2019/09/28 Javascript
ES6中的Javascript解构的实现
2020/10/30 Javascript
nuxt 页面路由配置,主页轮播组件开发操作
2020/11/05 Javascript
[45:56]Ti4正赛第一天 VG vs NEWBEE 3
2014/07/19 DOTA
python字符串加密解密的三种方法分享(base64 win32com)
2014/01/19 Python
在Ubuntu系统下安装使用Python的GUI工具wxPython
2016/02/18 Python
Python tkinter实现的图片移动碰撞动画效果【附源码下载】
2018/01/04 Python
pandas 实现将重复表格去重,并重新转换为表格的方法
2018/04/18 Python
Django添加favicon.ico图标的示例代码
2018/08/07 Python
python无限生成不重复(字母,数字,字符)组合的方法
2018/12/04 Python
浅谈ROC曲线的最佳阈值如何选取
2020/02/28 Python
使用pytorch实现论文中的unet网络
2020/06/24 Python
Gina Bacconi官网:吉娜贝康尼连衣裙和礼服
2018/04/24 全球购物
日本乐天德国站:Rakuten.de
2019/05/16 全球购物
个人求职简历的自我评价范文
2013/10/09 职场文书
社区安全生产月活动总结
2014/07/05 职场文书
农村结婚典礼主持词
2015/06/29 职场文书
旷工检讨书大全
2015/08/15 职场文书
2016年教师节感恩寄语
2015/12/04 职场文书
2019入党申请书范文3篇
2019/08/21 职场文书
python用海龟绘图写贪吃蛇游戏
2021/06/18 Python
Vue深入理解插槽slot的使用
2022/08/05 Vue.js