尝试理解一下MVC

简介

MVC (Model View Controller), 即 模型 视图 控制器,我觉得这里可以用 内容行为样式分离 来简单理解一下

  • 用 html 来写样式?
    那么整个 html 中就会充斥着很多无用的标签, html 的逻辑结构会变得很复杂
  • 用 css 来写内容?

    • 代码如下

      div::after {
      content: 'hello world';
      }

      这样在浏览器呈现的页面上会选不中这个 div,并且 JS 也取不到

  • 用 css 表示行为??
    IE 时代有过,不过这个 css 不仅要表示样式,还要控制一些复杂逻辑,会使得整个页面加载变慢
  • JS 控制样式??像如下代码

    var $div = $('#x');
    $div.hide();
    $div.show();

    那么上面代码 hide 之后 div.style.display 就不确定了

模块化

通过前面的描述,我们就知道,需要代码进行模块化了,而想让代码模块之间没有影响,必须要做到如下几点

  • 使用立即执行函数将模块包起来

    1. 我们不想要全局变量
    2. 我们要使用局部变量
    3. ES 5 里面,只有函数有局部变量
    4. 于是我们声明一个 function xxx,然后 xxx.call()
    5. 这个时候 xxx 是全局变量(全局函数)
    6. 所以我们不能给这个函数名字
    7. function(){}.call()
    8. 但是 Chrome 报错,语法错误
    9. 试出来一种方法可以不报错:
      i. !function(){}.call() (我们不在乎这个匿名函数的返回值,所以加个 ! 取反没关系)
      ii. (function(){}).call() 不推荐

      xxx
      (function(){}).call() 报错,这种方法会被浏览器当作
      xxx(function(){}).call() 执行

      iii. frank192837192463981273912873098127912378.call() 不推荐, 就是函数名后加一串随机数

  • 使用闭包
    如以下代码

    // 模块一定义
    !function() {
    var person = {
    name: 'frank',
    age: 18
    }
    window.frankGrowUp = function() {
    person.age += 1;
    return person.age;
    };
    }.call();
    // 模块二调用
    !function() {
    var newAge = window.frankGrowUp();
    console.log(newAge); // 19
    }.call();

    从以上代码可以看出

    1. 立即执行函数使得 person 无法被外部访问
    2. 闭包使得匿名函数可以操作 person
    3. window.frankGrowUp 保存了匿名函数的地址
    4. 任何地方都可以使用 window.frankGrowUp
      => 任何地方都可以使用 window.frankGrowUp 操作 person,但是不能直接访问 person
  • 然后用 js 文件将这些代码包起来
    就是将代码写入到这些 js 文件中,然后在页面上用 script 标签引入即可,注意 js 文件名要与实现代码功能相关

MVC

view 就相当于 html,就是能看得见的那一块东西
controller 属于操作 view 的一些事件逻辑用的,一般 controller 的写法如下

let view = document.querySelector();
let controller = function(view) {
// 操作 view 的代码逻辑
...
}
// 调用 controller
controller.call(view);

更高级一点的写法

let view = document.querySelector();
let controller = {
view: null,
init: function(view) {
this.view = view;
this.bindEvents(); // 调用定义的事件绑定,这里相当于代码 this.bindEvents.call(this);
},
bindEvents: function() {
let view = this.view;
window.addEventListener('事件', function(){
// 绑定事件操作逻辑代码
...
// active or deactive code according to your condition
});
}
active: function() {
// 激活添加类
},
deactive: function() {
// 去激活添加类
}
};

// 调用 controller, 这里代码相当于 controller.init.call(controller, view);
controller.init(view);

继续优化,使用箭头函数,那么箭头函数外的 this 就可以拿到箭头函数里面使用,而不用担心 this 的指向问题,即箭头函数内外 this 不变,如

...
bindEvents: function() {
let view = this.view;
window.addEventListener('事件', ()=> {
console.log(this); // this 在这里就指向 controller
// 绑定事件操作逻辑代码
...
});
}
...

Model 就是模型,跟数据相关,一般来说它的写法是这样

var model = { // 获取数据
fetch: function(){
...
return 一个 Promise 对象
},
save: function(name, content) { // 保存数据
...
// 存入数据
return 一个 Promise 对象
}
};

整个 MVC 的逻辑图如下所示

总结代码

Model

window.Model = function(object){
let resourceName = object.resourceName;
return {
init: function () {
},
fetch: function () {
},
save: function (object) {
}
}
}

View

window.View = function(xxx){
return document.querySelector(xxx);
}

Controller

window.Controller = function(options) {
var init = options.init;
let object = {
view: null,
model: null,
init: function(view, model){
this.view = view;
this.model = model;
this.model.init();
init.call(this, view, model);
this.bindEvents();
},
};
for (let key in options) {
if (key !== 'init') {
object[key] = options[key];
}
}
return object;
}

或者也可以这么写

var model = {
data: null,
init(){}, // model 初始化
fetch(){}, // model 的数据获取
save(){} // model 的数据保存
}
var view = {
init() {}, // view 初始化
template: 'html 内容' // view 网页上的内容
}
var controller = {
view: null,
model: null,
init(view, model){
// 获取到 view 以及 model 的实例
this.view = view;
this.model = model;
// 分别调用 view 以及 model 的初始化函数对其进行初始化操作
this.view.init.call(this.view);
this.model.init.call(this.model);
// 最后绑定事件
this.bindEvents();
}
render(){
// 用来渲染 view
},
bindEvents(){
// 用来绑定事件
}
}

最后的总结

MVC 是一种设计模式,即 Model View Controller,具体来说他按照功能将代码分成三大块

  1. Model 层
    这一层负责跟 Server 打交道,与 Server 进行请求和响应,保证数据的存储和更新
  2. View 层
    View 层主要是用来展示给用户操作的,即一个操作的界面
  3. Controller 层
    这一层则用来综合控制 view 层以及 Model 层,它监听 view 层的变化,当 view 层有变化时则及时通知 Controller ,然后 Controller 调用 Model 来返回数据并同时更新 view, Molde 则再次请求 Server 进行数据的更新