MVC浅析

729 阅读6分钟

MVC是啥?

如何设计一个程序的结构,这是一门专门的学问,叫做架构模式(architectural pattern),属于编程的方法论,MVC就是架构模式的一种。
每个模块都可以写成三个对象,分别是M/V/C
M-Model(数据模型)负责操作所有数据
v-view(视图)负责所有UI界面
C-Controller(控制器)负责其他
MVC没有严格的定义,每个程序员对MVC的理解都不一样,但对着三个名字是没有分歧的

mvc伪代码

const m = {//数据层,关于数据的操作放在这里
    data:{
        n:parseInt(localStorage.getItem('number')||100)
    },//数据初始化
    update:function (data) {
        //更新数据的方法
    },
    delete:function (data) {
        //删除数据的方法
    },
    get:function (data) {
        //获得数据的方法
    }
}
const v = {//视图层,关于视图的操作放在这里
    el:'挂载点',
    html:'需要插入元素内的HTML内容',
    render('渲染html')
}
const c = {//控制层,关于事件监听的放到这里
    //找到重要的元素绑定事件
    //如果触发事件调用更改数据方法及渲染方法
    const a =$('找到A')
    const b = $('找到B')
    b.on('click',function(){
        //调用数据层方法更改数据
        //调用视图层方法渲染页面
    })
}

为什么用MVC

可能对于我们之前用惯写下面这种代码的同学来说用MVC的方式去实现某一个功能会觉得变得更复杂

//找到重要的元素
const $button1 = $("#add1");
const $button2 = $("#minus1");
const $button3 = $("#mul2");
//获得数据
const n = localStorage.getItem("n");
$number.text(n || 100);
//监听事件,改变数据
$button1.on("click", () => {
  let n = parseInt($number.text());
  n += 1;
  localStorage.setItem("n", n);
  $number.text(n);
});
$button2.on("click", () => {
  let n = parseInt($number.text());
  n -= 1;
  localStorage.setItem("n", n);
  $number.text(n);
});
//$button3.......

用上面这些代码写当然没问题,也很简单,想到啥写啥,但如果代码量一上来,我们想找到某个功能的相关代码对其进行修改就会变得非常麻烦,而且还存在变量污染,而且对于有些功能相似的代码,这完全是重复劳动呀,所以复用性低
MVC麻烦是麻烦了点,但是后期维护成本低,因为每一部分代码是以一个对象的方式储存在一个独立的空间,负责某一项功能,我们更容易找到对应代码,并且在其内部修改不会对外部的代码造成很大影响

模块化

上面我们说了MVC解决了随着代码量上升难维护,高重复的等问题
那么模块化就是再此基础上进行了再次的升级,如果随着功能更多,业务逻辑越来越复杂,代码更也会变得更复杂,我们如果用对象的方式去区分某个功能已经不现实了,光变量名都记不过来,显示屏的代码就算折叠起来下拉不超过2页,你估计都头炸,更别说复杂的业务逻辑
所以如果我们的代码有清晰的脉络就会方便很多

模块化
看~ 有了清晰的脉络,我们在查找某个功能对应的代码区就很方便,而且修改对应功能的代码对其他代码文件的不会有影响
就像我们玩的积木一样,各个积木可以组合在一起形成一个形状,又可以拆分,又可以替换,因为基本上各个积木块都是独立的,只要他们之间的接口(形装)匹配,就可以灵活地组合子啊一起,这也叫解耦,当然这是理想的状态,解耦是逐渐达到这个理想的状态。
模块之间的联系越少越好,接口越简单越好(低耦合,细线通信)。如果划分的模块之间接口很复杂,说明功能划分得不太合理,模块之间的耦合太高了,同时也说明某模块的内聚也不高。

EventBus

上面我们说了模块化,既然我们把每个功能都分成不同的模块(文件),那么问题来了。。。如果文件C中检查到用户的操作,需要通知文件M修改数据,M修改了数据需要通知文件V进行页面渲染怎么办?当然是eventbus。。。不管是jquery还是vue中都有类似于eventbus的存在,只不过叫法不一样,不过它们的功能是相似的,都是负责组件(模块)中的通信

eventbus也是一种设计模式或者框架,主要用于组件/对象间通信的优化简化。 下面演示用jquery生成的eventbus
eventbus包含很多方法,on方法可以监听事件,trigger方法可以触发事件,off卸载监听

//伪代码
import $ from 'jquery'
const eventbus = $(window)//返回一个eventbus的所有方法的对象

const model = {//数据层
    data:{'数据':1},
    update(data){
        Object.assign(model.data,data)//更新数据
        eventbus.trigger('更新数据')//触发事件 
    }
    
}
const View = {
    el:'挂载点',
    html:'<div>{{内容}}</div>',
    init(container){
        View.el = $(container)
    }
    render(data){
        $(View.html.replace('{{n}}', n))
        .appendTo(View.el)//更换新的内容渲染进页面
    }
}
const control = {
    init(container){
        View.init(container)
        View.render(m.data.n)//初始化页面
        autoBindEvents()
        eventbus.on('更新数据',()=>{//监听数据层的eventbus.trigger如果有被触发说明数据有更新从而进行渲染
            View.render(m.data.n)
        })
    },
    
    add() {
        m.update({n: m.data.n + 1})
    },//改变数据方法
    minus() {
        m.update({n: m.data.n - 1})
    },//改变数据
    //监听改变数据的按钮
    autoBindEvents() {
        View.el.on('click','app1','add')
    }
}
//当然当需求更复杂的时候我们可以将eventbus写成一个类,让生成的实例对象继承eventbus,这样每个对象都拥有了可以触发和监听的功能,相当灵活

eventbus我们可以用第三方的库,也可以自己手写,根据实际情况来决定

表驱动编程

定义:表驱动编程是一种使我们可以再表中查找信息,而不必写很多逻辑语句(if或case)来把它们找出来的方法,事实上,任何信息都可以通过表来挑选,再简单情况下用逻辑语句是更简单的,但是一旦需要判断条件增多那我们可能要写大量重复的判断语句,这时候我们适当的用表中提取出的值来写条件判断将事半功倍。

数组中的应用

假设你需要一个可以返回每个月中天数的函数)
常规写法

月份
表驱动写法

表驱动写法
可以看到我们通过表驱动编程将代码量缩减到9行,而且还解决了闰年的问题

对象的应用

假设我们现在要监听某个元素对其绑定事件
我们可能要这么写

document.querySelector('#add1').addEventListener('click', add1)
document.querySelector('#min1').addEventListener('click', min1)
document........

如果监听事件有10个?100个,写的太累了
通过表驱动的方式我们可以写成下面这样

const Controller = {
   events: {//表驱动编程,对象
    "#add1 click": "add1",//key的前半部分为要监听的元素,后半部分为监听的事件,value为要执行的方法
    "#min1 click": "min1"
  },
  autoBindEvents() {
    Object.keys(this.events).forEach(key => {//遍历对象获得对应的值去做赋值操作
      const value = this.events[key];
      const handler = this[value];
      const [selector, event] = key.split(" ");
      $(".buttons").on(event, selector, handler);//将提取出来的值去监听事件
    });
  },
}

总之程序员拒绝重复,程序员追求稳定的简单。
简单总结:写代码就像写作文,好的作品通俗易懂没有废话,条理清晰,落落大方。