浅析 MVC

132 阅读3分钟

MVC

MVC是一种设计模式,设计模式是对通用代码的别称。

  • M(Model)——数据模型,负责操作所有的数据。
// 以伪代码示例
const m = {
    data:{n : parseInt(localStorage.getItem('n'))},//程序中涉及需要的数据
    create(){ 增数据 },
    delete() { 删数据 },
    update(x) { //更新数据
    	m.data.n = x
        eventBus.trigger('m:updated')  
        localStorage.setItem('n',m.data.n)
    },
    get(){获得数据}
}
  • V(View)—— 视图,负责所有UI页面。需要html、render等。

所有的视图都是数据的渲染(view = render(data),但是render渲染很粗犷)。

//以下列伪代码为例
const v = {
            el:需要渲染的容器,
            html1:`页面内容`,
        render(data){渲染页面}    
        }
  • C(Controller)——控制器,定义用户界面对用户输入的响应方式,起到不同层面间的组织作用,用于控制应用程序的流程,处理用户的行为和数据model上的改变。
//伪代码示例
const c = {
    init(container){  //c的初始化
        v.init(container)//给v传入了容器也进行初始化
         v.render(m.data.n)  //view = render(data) 
         eventBus.on('m:updated',()=>{ //监听到数据变化后重新渲染
            v.render(m.data.n)
         }   
    },
    events:{事件以哈希表记录},
    add(){m.data.n+=1},
    minus(){m.data.n -=1},
    mul(){m.data.n *=2},
    div(){m.data.n /=2},
    autoBindEvents(){自动绑定事件}
 }    

MVC中三部分的代码是有交集的,有时候也可以合并编写,不是一定要分成M、V、C三个部分。

EventBus

EventBus 可以实现对象之间的通信,如果当数据或某些特性发生改变时触发事件,另一个地方监听事件那就可以做到通信了。 常用的API有:

  • on——监听事件;
  • trigger——触发事件;
  • off——取消监听
示例:
const eventBus = $(window)

eventBus.trigger('pikapika')//触发事件,事件名为pikapika
eventBus.on('pikapika',()=>{  //监听到事件pikapika,打印内容
           console.log('去吧皮卡丘')
})
eventBus.off('pikapika')//取消监听此事件

EventBus的封装

EventBus 可以使代码更加格式化,尤其在模块化的操作中,封装 + 继承这些就非常重要。 下面是对EventBus的封装:

import $ from 'jquery'
class EventBus{
    constructor(){
        this._eventBus = $(window)  
    }
    on(eventName, fn){
        return this._eventBus.on(eventName, fn)
    }
    trigger(eventName, data){
        return this._eventBus.trigger(eventName, data)
    }
    off(eventName, fn){
        return this._eventBus.off(eventName, fn)
    }
}
export default EventBus

class EventBus的继承

可以使class Model继承 class EventBus,但是子类constructor里要调用 super 方法,因为子类自己的 this 对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其加上子类自己的实例属性和方法。

import EventBus from './EventBus.js'
class Model extends EventBus{
    constructor(options){
        super(); // super 关键字,它在这里表示父类的构造函数,用来新建父类的 this 对象
        ......

表驱动编程

表驱动编程是从大量相似的代码中抽取出本质的东西,组成哈希表,利用表进行编程,以减少重复代码。表驱动编程的意义在于逻辑与数据的分离。

原来的加减乘除事件:

v.on('click','#add1',()=>{
           m.data.n+=1;
})  
v.on('click','#minus1',()=>{
           m.data.n-=1;
})
v.on('click','#mul2',()=>{
           m.data.n*=2;
})
v.on('click','#divide2',()=>{
           m.data.n/=2;
})

将其中的关键信息编入哈希表中:

const c ={
    events:{
          'click #add1': 'add',
          'click #minus1': 'minus',
          'click #mul2': 'mul',
          'click #divide2': 'div'
      },
      add(){m.data.n+=1},
      minus(){m.data.n -=1},
      mul(){m.data.n *=2},
      div(){m.data.n /=2},
};

执行哈希表中的事件:

autoBindEvents(){
      for(let key in c.events){
          const spaceIndex = key.indexOf(' ')
          const part1 = key.slice(0,spaceIndex)
          const part2 = key.slice(spaceIndex + 1) // 因为不能包含空格所以下标+1
          const value = c[c.events[key]] 
          // c.events[key]——字符串,c[c.events[key]]——对应的操作
          v.on(part1, part2, value)
      }
}

表驱动编程提高了程序的可读性,减少了重复代码,也更容易扩展内容。

关于模块化

从业务角度来看,一个项目就是由各个模块组合而成,如果新开发的项目可以复用原先的模块,就可以减少工作量,也有利于代码维护,出现了错误也可以找到对应的模块里去修改。

按照这个思路,开发新项目时,开发人员的工作从原来的单纯的开发功能变为:接入已有的功能模块,开发不存在的功能模块。但是开发模块也有要求:

  • 模块独立可运行;
  • 模块可满足扩展的需求。 为了让不同的项目去实现各自的需求,又不重复开发通用代码,模块需要做到独立,互不影响,代码结构清晰。