JavaScript中MVC模式详解与下拉框实现

作者:半夏时间:2026-10-29 21:04:23浏览:138

本文深入解析JavaScript中的MVC软件架构模式,详细阐述模型、视图与控制器的职责划分。通过一个支持增删操作的下拉框控件实例,展示如何利用观察者模式实现数据与界面的解耦,并分析该模式在耦合性、可维护性及开发成本方面的优缺点。

MVC模式核心概念解析

MVC(Model-View-Controller)是软件工程中常用的一种架构模式,其核心思想是将软件系统划分为三个主要部分:模型(Model)、视图(View)和控制器(Controller)。

模型(Model)负责封装与应用程序业务逻辑相关的数据,以及处理数据的方法。模型拥有对数据的直接访问权限,且不依赖视图和控制器,这意味着模型无需关心页面如何显示或如何被用户操作。

视图(View)的主要职责是监听模型层的数据变化,并实时更新HTML页面。此外,视图层还负责处理用户事件的注册以及发起Ajax请求等发布事件的操作。

控制器(Controller)负责接收用户的操作指令,主要工作是订阅视图层的事件。当页面触发事件时,控制器不直接输出内容或处理页面,而是接收请求后决定调用模型中的哪个方法来处理数据,并确定调用视图中的哪个方法来展示返回结果。

基于下拉框的MVC实战实现

接下来,我们将通过实现一个简单的下拉框控件来演示MVC模式,该控件支持数据的增加和删除操作,具体效果如下图所示:

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()来更新页面上的数据。

MVC模式的优缺点分析

模型层(Model)主要负责业务数据的封装。视图层(View)主要发布事件及监听模型层的数据,一旦模型层数据改变,视图层会及时更新页面显示。控制层(Controller)主要监听视图层(View)的事件,调用模型层(Model)的方法来更新模型数据。模型层数据更新后会发布消息,视图层通过监听模型层的数据变化来更新页面显示。上述即为MVC的基本流程。MVC的优点包括:1. 耦合性低:视图层与业务层分离,若页面显示改变,只需在视图层修改,无需改动模型层和控制层代码,从而易于更改应用层的数据层和业务规则。2. 可维护性:视图层与业务逻辑层的分离使得Web应用更易于维护和修改。MVC的缺点包括:个人觉得该模式适合大型项目,对于中小型项目并不适用,因为实现简单的增删改操作仅需少量JS代码,但采用MVC模式会显著增加代码量,同时也提高了学习成本。当然,如果使用封装好的MVC库或框架,情况会有所改善。

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

作者最新文章