# javascript中的MVC

MVC 是一种使用 MVC(Model View Controller 模型-视图-控制器)设计创建 Web 应用程序的模式:

  • Model(模型)是应用程序中用于处理应用程序数据逻辑的部分,通常模型对象负责在数据库中存取数据。
  • View(视图)是应用程序中处理数据显示的部分,通常视图是依据模型数据创建的。
  • Controller(控制器)是应用程序中处理用户交互的部分,通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。

# MVC的基础是观察者模式,这是实现model和view同步的关键

借助观察者模式,实现在调用model的set方法改变其值的时候,模板也同步更新,但这样的实现却很别扭,因为需要手动监听model值的改变(通过watch方法)并传入一个回调函数,在使用binding方法,绑定model和view

<div id="div1"></div>
<div id="div2"></div>
<script>
	function Model(value) {
	    this._value = typeof value === 'undefined' ? '' : value;
	    this._listeners = [];
	}
	Model.prototype.set = function (value) {
	    var self = this;
	    self._value = value;
	    // model中的值改变时,应通知注册过的回调函数
	    // 按照Javascript事件处理的一般机制,我们异步地调用回调函数
	    // 如果觉得setTimeout影响性能,也可以采用requestAnimationFrame
	    setTimeout(function () {
	        self._listeners.forEach(function (listener) {
	            listener.call(self, value);
	        });
		});
	};
	Model.prototype.watch = function (listener) {
	    // 注册监听的回调函数
	    this._listeners.push(listener);
	};
	Model.prototype.binding = function (node) {
	    // 将watch的逻辑和通用的回调函数放到这里
	    this.watch(function (value) {
	        node.innerHTML = value;
	    });
	};


  var model = new Model();
  model.binding(document.getElementById('div1'));
  model.binding(document.getElementById('div2'));
  model.set('this is a div');
</script>

# 实现controller,将绑定从逻辑代码中解耦

<div id="div1" bind="model1"></div>
<div id="div2" bind="model1"></div>
<script type="text/javascript">
  function Controller(callback) {
    var models = {};
    // 找到所有有bind属性的元素
    var views = document.querySelectorAll('[bind]');
    // 将views处理为普通数组
    views = Array.prototype.slice.call(views, 0);
    views.forEach(function (view) {
        var modelName = view.getAttribute('bind');
        // 取出或新建该元素所绑定的model
        models[modelName] = models[modelName] || new Model();
        // 完成该元素和指定model的绑定
        models[modelName].binding(view);
    });
    // 调用controller的具体逻辑,将models传入,方便业务处理
    callback.call(this, models);
  }
  new Controller(function (models) {
      var model1 = models.model1;
      model1.set('this is a div');
  });
</script>

# 整合代码

function Model(value) {
    this._value = typeof value === 'undefined' ? '' : value;
    this._listeners = [];
}
Model.prototype.set = function (value) {
    var self = this;
    self._value = value;
    setTimeout(function () {
        self._listeners.forEach(function (listener) {
            listener.call(self, value);
        });
    });
};
Model.prototype.watch = function (listener) {
    this._listeners.push(listener);
};
Model.prototype.binding = function (node) {
    this.watch(function (value) {
        node.innerHTML = value;
    });
};
function Controller(callback) {
    var models = {};
    var views = Array.prototype.slice.call(document.querySelectorAll('[bind]'), 0);
    views.forEach(function (view) {
        var modelName = view.getAttribute('bind');
        (models[modelName] = models[modelName] || new Model()).binding(view);
    });
    callback.call(this, models);
}

# 简单使用

<span bind="hour"></span> : <span bind="minute"></span> : <span bind="second"></span>
<script type="text/javascript">
// controller:
new Controller(function (models) {
    function setTime() {
        var date = new Date();
        models.hour.set(date.getHours());
        models.minute.set(date.getMinutes());
        models.second.set(date.getSeconds());
    }
    setTime();
    setInterval(setTime, 1000);
});
</script>

controller中只负责更新model的逻辑,和view完全解耦;而view和model的绑定是通过view中的属性和框架中controller的初始化代码完成的,也没有出现在业务逻辑中;至于view的更新,也是通过框架中的观察者模式实现的。

在简单的系统中应用MVC模式,会增加结构的复杂性,并且降低效率