关于mvc,我所知道的

445 阅读5分钟

MVC

MVC是什么?

一种设计模式,它的设计思路是将模块分成三个对象,分别是M、V、C:

  • M-Model(数据模型):负责操作所有数据;
  • V-view(视图):负责所有UI界面的事情;
  • C-Controller(控制器):负责操作M、V以及其他,即业务逻辑;

MVC解决了什么问题?

减少代码的重复及冗余。

如何实现mvc式代码?

Model

let m = {
    data: { '程序需要操作的数据或信息' },
    create(){ '增数据' },
    delete(){ '删数据' },
    update(data) {
       //新数据替换旧数据
       Object.assign(m.data, data)
       //eventBus触发'myEvent'自定义事件 
       eventBus.trigger('myEvent')
    },
    get(){ '获取数据' } 
}

View

let v = {
    el: '被操作的元素节点',
    html: `<div>要添加到el的内容</div>`
    init(){'页面的初始化'},
    render(data){ '渲染页面' }
}

Contorller

let c = {
   init(){
      // 初始化 v
      v.init()
      // v 不关心数据的变化,所以由c传入数据进行渲染
      v.render(data)
      c.autoBindEvents()
      // 当eventBus触发'myEvent'自定义事件时进行渲染操作
      eventBus.on('myEvent', () => { v.render(data) }) 
   },
   events:{ '会被触发的事件以哈希表方式记录' },
   autoBindEvents() { '事件绑定' }
}

完整代码

EventBus

mvc式编写代码可以减少代码重复,而EventBus是其中一个帮手。

可以想象,如果上面的代码没有eventBus,那么在更新数据的时候,c还需要手动调用一下v.render()方法,这多麻烦,如果代码量不大还好,要渲染的地方多了,就非常难受了。

那EventBus到底是什么呢?用DOM事件或jQuery的事件监听的时候,是不是非常像EventBus呢?我们给监听函数on或addEventListener提供一个要被响应的事件和响应函数,等事件被响应的时候响应函数被执行,但这不仅是EventBus,而是一种更为高级的实现-“订阅发布模式”。关于“订阅发布模式”这里就不做展开了。结论就是EventBus是自定义事件绑定与触发的机制,通过一个可绑定事件的对象来实现这个功能。

用最简单的方式理解EventBus:

  1. on:监听自定义事件,绑定事件响应函数;
  2. trigger:在某个地方触发自定义事件。

enentBus的函数一般有:

  1. on(事件的绑定)
// 事件列表对象
const evetList = {}
function on(eventName,callback) {
    if(!(eventName in eventList)) {
        // 事件没有记录过,则为这个事件创建一个新的通知列表
        eventList[EventName] = []
    }
    // 将响应函数callback放到eventName的通知列表中,等待被触发
    eventList[EventName].push(callback)
}
  1. emit/trigger(事件的触发)
const eventList = {}
const trigger = (eventName,params)=>{
    // 找到与eventName对应的通知列表,将params传给通知列表内的每个响应函数
    if(eventList[eventName]){
         let arr = eventList[eventName];
         arr.forEach(callback => {
             callback(params)
         })
    }
}
  1. off(事件的解绑)
const eventList = {}
const off = (eventName,callback) => {
    if(eventList[eventName]) {
        let index = eventList[eventName].indexof(callback)
        eventList[eventName].splice(index,1)
    }
}

表驱动编程

先说结论:表驱动编程是一种编程方法,用来消除代码中频繁的if elseswitch case。不仅如此,这种编程方式可以延伸其他地方,比如DOM的事件绑定,多个button的click事件也可以通过表驱动的方式绑定。

和mvc一样,表驱动编程也是用来减少代码冗余的,它是对重复事情的精简。

可以粗暴的理解为它类似数据库表,这样在代码的编写过程中就可以实现数据和逻辑的分离了。

什么时候可以用到表驱动?

对多个数据有相同处理逻辑的时候就可以用了,此时将数据抽离出来形成“表”,这个表可以是数组、hash表等数据结构。

举个栗子:

let arr = [1,2,3,4,5]

// 打印输出arr的每个元素内容
// 两种处理方式
// 方式一:
console.log(arr[0])
console.log(arr[1])
console.log(arr[2])
console.log(arr[3])
console.log(arr[4])

// 方式二:
arr.forEach(item => {
    console.log(item)
})

表驱动编程的好处

  1. 减少代码冗余;
  2. 数据与逻辑分离,数据的增加不会造成逻辑代码的增加
  3. 方便维护代码

模块化

为什么要模块化

  • 可以避免命名冲突
  • 灵活架构,每个模块完成一个特定的子功能,所有的模块按某种方法组装起来,成为一个整体,完成整个系统所要求的功能。
  • 提高复用性和可维护性

比如说将代码重构为mvc式,m、v、c也是模块,首先模块化可以让文件更有组织,哪个模块出了问题就去哪个模块解决问题,其次是用模块的时候我们不用关心模块内的具体实现,使用模块暴露出的接口就可以了。

模块化的规范

  1. RequireJS模块:RequireJS遵循的是AMD规范。AMD规范是异步加载,依赖前置,特点是准备充分,但加载会较慢。使用define()定义模块,require()加载模块。

  2. SeaJS模块:SeaJS遵循的是CMD规范。CMD规范也会异步加载,不同的是CMD依赖就近,特点是首次加载很快。CMD规范也是使用define()定义模块,require()加载模块。但和AMD规范思想不同,写法也不同。

  3. CommonJS:CommonJS规范使用require加载模块,module.exports导出模块,module可省略,但不推荐。特点是加载模块顺序按照词法解析的顺序加载,是同步加载的。

  4. ES6模块:ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系。使用import和export来导入导出,ES6还提供了一个default,用来提供默认的export。特点是加载模块存储的是值的引用,所以全局只有一份;加载模块也是异步的。ES6的module吸收了CommoneJS和AMD两者的优点,兼容两标准的规范。