[关闭]
@brizer 2016-02-21T11:04:34.000000Z 字数 4069 阅读 1130

简单理解javascript中的MVC


前言

用过一些MVC的框架,一直知其然不知其所以然,正好看到两篇不错的文章,于是针对文章中的内容,进行一定的理解,来初步认识MVC的原理。


MVC

其实在后端开发中,MVC已经是轻车熟路了,笔者自己也做过PHP的MVC框架,不过性能就不敢保证了。

所以MVC的基本概念就不再提了。M主要是处理数据,V是监听模型上的改变,从而更新html,C则是接受用户的操作,通过监听V上的用户行为来调用M或V完成用户的操作。


发布/订阅

上面说到了监听,javascript中都是通过事件监听的,我们需要让多个对象按照一定的配置来监听各自的目标,就需要用到发布/订阅模式。

  1. /*
  2. 下面是观察者模式类,它又叫发布---订阅模式;它定义了对象间的一种一对多的关系,
  3. 让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知。
  4. */
  5. function Event(observer) {
  6. this._observer = observer;
  7. this._listeners = [];
  8. }
  9. Event.prototype = {
  10. constaructor: 'Event',
  11. attach : function(listeners) {
  12. this._listeners.push(listeners);
  13. },
  14. notify: function(objs){
  15. for(var i = 0,ilen = this._listeners.length; i < ilen; i+=1) {
  16. this._listeners[i](this._observer,objs);
  17. }
  18. }
  19. };

attach是用来指定监听者的,而nofify则同来通知各个监听者。

我们以一个实例来看看MVC是如何工作的:

完整demo地址


model

我们通过模型来处理数据相关的操作:

  1. /*
  2. 模型用于封装与应用程序的业务逻辑相关的数据以及对数据处理的方法。模型有对数据直接访问的权利。
  3. 模型不依赖 "视图" 和 "控制器", 也就是说 模型它不关心页面如何显示及如何被操作.
  4. */
  5. function Mode(elems) {
  6. // 所有元素
  7. this._elems = elems;
  8. // 被选中元素的索引
  9. this._selectedIndex = -1;
  10. // 增加一项
  11. this.itemAdd = new Event(this);
  12. // 删除一项
  13. this.itemRemoved = new Event(this);
  14. this.selectedIndexChanged = new Event(this);
  15. }
  16. Mode.prototype = {
  17. constructor: 'Mode',
  18. // 获取所有的项
  19. getItems: function(){
  20. return [].concat(this._elems);
  21. },
  22. // 增加一项
  23. addItem: function(elem) {
  24. this._elems.push(elem);
  25. //通知视图进行改变
  26. this.itemAdd.notify({elem:elem});
  27. },
  28. // 删除一项
  29. removeItem: function(index) {
  30. var item = this._elems[index];
  31. this._elems.splice(index,1);
  32. this.itemRemoved.notify({elem:item});
  33. if(index === this._selectedIndex) {
  34. this.setSelectedIndex(-1);
  35. }
  36. },
  37. getSelectedIndex: function(){
  38. return this._selectedIndex;
  39. },
  40. setSelectedIndex: function(index){
  41. var previousIndex = this._selectedIndex;
  42. this._selectedIndex = index;
  43. this.selectedIndexChanged.notify({previous : previousIndex});
  44. }
  45. };

可以看到,我们在构造函数中对每个操作声明了新的发布订阅者Event,并在对应方法中进行了具体数据操作和事件通知notify。


view

接下来我们定义视图:

  1. /*
  2. * 视图显示模型数据,并触发UI事件。
  3. */
  4. function View(model,elements){
  5. this._model = model;
  6. this._elements = elements;
  7. this.listModified = new Event(this);
  8. this.addButtonClicked = new Event(this);
  9. this.delButtonClicked = new Event(this);
  10. var that = this;
  11. // 绑定模型监听器
  12. this._model.itemAdd.attach(function(){
  13. that.rebuildList();
  14. });
  15. this._model.itemRemoved.attach(function(){
  16. that.rebuildList();
  17. });
  18. // 将监听器绑定到HTML控件上
  19. this._elements.list.change(function(e){
  20. that.listModified.notify({index: e.target.selectedIndex});
  21. });
  22. // 添加按钮绑定事件
  23. this._elements.addButton.click(function(e){
  24. that.addButtonClicked.notify();
  25. });
  26. // 删除按钮绑定事件
  27. this._elements.delButton.click(function(e){
  28. that.delButtonClicked.notify();
  29. });
  30. }
  31. View.prototype = {
  32. constructor: 'View',
  33. show: function(){
  34. this.rebuildList();
  35. },
  36. //重新构建list
  37. rebuildList: function(){
  38. var list = this._elements.list,
  39. items,
  40. key;
  41. list.html("");
  42. items = this._model.getItems();
  43. for(key in items) {
  44. if(items.hasOwnProperty(key)) {
  45. list.append('<option value="'+items[key]+'">' +items[key]+ '</option>');
  46. }
  47. }
  48. this._model.setSelectedIndex(-1);
  49. }
  50. };

我们发布了一些事件,也监听了模型中事件并进行对应的操作。比如监听itemAdd后进行rebulidList操作重新构建list。而addButton,则是通过发布事件给控制器来监听从而在控制器中调用模型进行数据操作。


controller

最后看看控制器:

  1. /*
  2. 控制器响应用户操作,调用模型上的变化函数
  3. 负责转发请求,对请求进行处理
  4. */
  5. function Controller(model,view) {
  6. this._model = model;
  7. this._view = view;
  8. var that = this;
  9. //控制器订阅通知并对请求进行处理
  10. this._view.listModified.attach(function(sender,args){
  11. that.updateSelected(args.index);
  12. });
  13. this._view.addButtonClicked.attach(function(){
  14. that.addItem();
  15. });
  16. this._view.delButtonClicked.attach(function(){
  17. that.delItem();
  18. });
  19. }
  20. Controller.prototype = {
  21. constructor: 'Controller',
  22. //具体的处理函数,调用模型进行对应的数据处理
  23. addItem: function(){
  24. var item = window.prompt('Add item:', '');
  25. if (item) {
  26. this._model.addItem(item);
  27. }
  28. },
  29. delItem: function(){
  30. var index = this._model.getSelectedIndex();
  31. if(index !== -1) {
  32. this._model.removeItem(index);
  33. }
  34. },
  35. updateSelected: function(index){
  36. this._model.setSelectedIndex(index);
  37. }
  38. };

控制器负责转发,调用模型的方法来处理数据。


部署

我们接下来只需要定义html:

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

和完成初始化即可:

  1. //---------------------------------
  2. //初始化
  3. $(function () {
  4. var model = new Mode(['PHP', 'JavaScript']),
  5. view = new View(model, {
  6. 'list' : $('#list'),
  7. 'addButton' : $('#plusBtn'),
  8. 'delButton' : $('#minusBtn')
  9. }),
  10. controller = new Controller(model, view);
  11. view.show();
  12. });

通过初始化将html中的节点对象和view中的参数对应起来即可。


感悟

绕来绕去看起来确实比较麻烦,但是对于大型项目来说,MVC的开发思想可以让开发者关注于数据和状态的变化,而不用在乎一些琐碎的细节,其实又是相当有帮忙的。

当然这个例子很不完善,也没有复用性,但是却简单的让人知道了MVC的基本原理,还是很有收获的。


参考

理解javascript中的mvc

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注