浅析 MVC

143 阅读2分钟

什么是MVC?

MVC是一种设计模式,主要分为M V C 三块。

M:model(数据模型),负责操作所有的数据。

V:view(视图),负责所有UI界面。

C:control(控制器),负责除M和V以外的东西。

我们以一个很简单的小项目举例:

点击按钮会对数字进行计算。

//不使用MVC(伪代码)
let n = result.text() //100
+1.on('click',()=>{
    n += 1
    localStorage.setItem('number',n)
    result.render()
})
-1.on('click',()=>{
    n -= 1
    localStorage.setItem('number',n)
    result.render()
})
*2.on('click',()=>{
    n *= 2
    localStorage.setItem('number',n)
    result.render()
})
/2.on('click',()=>{
    n /= 2
    localStorage.setItem('number',n)
    result.render()
})
//使用MVC
const m = {
    n : parseInt(localStorage.getItem('number')) || 100
}

const v = {
    render(data){
        //将data渲染到页面
        localStorage.setItem('number',data)
    }
}

const c = {
    btn.on('click','-1' ()=>{
        n -= 1
        render(m.n)
    }),
    btn.on('click','+1' ()=>{
        n += 1
        render(m.n)
    }),
    btn.on('click','*2' ()=>{
        n *= 2
        render(m.n)
    }),
    btn.on('click','/2' ()=>{
        n /= 1
        render(m.n)
    })
}

我们可以看到,使用MVC之后,每一个模块都只需要负责自己那一部分的代码即可。

表驱动式编程

我们可以看到上面的代码还是略显繁琐,因为多次调用了on监听事件,我们可以使用表驱动式来将其简化。

//简化前
const c = {
    btn.on('click','-1' ()=>{
        n -= 1
        render(m.n)
    }),
    btn.on('click','+1' ()=>{
        n += 1
        render(m.n)
    }),
    btn.on('click','*2' ()=>{
        n *= 2
        render(m.n)
    }),
    btn.on('click','/2' ()=>{
        n /= 1
        render(m.n)
    })
}
//简化后
const c = {
    init(){
        c.bilndEvents()
    }
    
    hash:{
        'click -1':sub,
        'click +1':add,
        'click *2':mul,
        'click /2':div,
    },
    //表
    bilndEvents(){
      for(let key in c.hash){
          const value = c.hash[key]
          btn.on(key[0],key[1],value,)
      }    
    },
    sub(){
        v.render(m.n - 1)
    },
    add(){
        v.reder(m.n + 1)
    },
    mul(){
        v.render(m.n * 2)
    },
    div(){
        v.render(m.n / 2)
    }
}

//c.init()

可以看到,使用表驱动式编程后,我们就不在需要去重复监听事件了,并且,代码的逻辑也变得十分清晰,想知道事件如何处理,只需要看一下驱动表即可。

eventBus

上面的代码还有一个问题,它和简化前一样,每次都需要获取n,然后将其渲染进页面。我们可以通过eventBus来监听数据n的变化。

const eventBus = $(window)
//创建eventBus
const m = {
    n : parseInt(localStorage.getItem('number')) || 100,
    update(data){
        Object.assign(m.n, data)
        eventBus.trigger('updated')
        //n变化时触发
    }
}

const c = {
    init(){
        c.bilndEvents()
        eventBus.on('updated',()=>{
            v.render(m.n)
        })
        //一旦触发监听,就重新渲染
    }
    
    hash:{
        'click -1':sub,
        'click +1':add,
        'click *2':mul,
        'click /2':div,
    },
    sub(){
        m.update(m.n - 1)
    },
    add(){
        m.update(m.n + 1)
    },
    mul(){
        m.update(m.n * 2)
    },
    div(){
        m.update(m.n / 2)
    },
    bilndEvents(){
      for(let key in c.hash){
          const value = c.hash[key]
          btn.on(key[0],key[1],value,)
      }    
    },
}

使用了eventBus后,事件不在需要关注渲染,只需每次将数字传给update即可。

eventBus监听其实是每一个模块都需要的一个功能,我们还可以通过继承的方式,将其放到M V C 的原型链上。

class EventBus {
    constructor(){
         this._eventBUs = $(window)
    }
    
    on(eventName,fn){
         return this._eventBus.on(eventName,dn)
    }
    
    trigger(eventName, data) {
         return this._eventBus.trigger(eventName, data)
  }

    off(eventName, fn) {
         return this._eventBus.off(eventName, fn)
  }
    
}

export default EventBus

我们常用的eventBus的api为 on , trigger , off

  1. eventBus.on('string',callbackname)
  2. eventBus.trigger('string')
  3. eventBus.off('string',callbackname) //若不传callbackname 即表示取消所有事件监听

模块化

当我们面临一项复杂的需求时,我们可以通过模块化将其分成多个模块(例如MVC),每个模块只负责自己的事情,不去了解外部的参数/数据。

使用这种方法构建的项目,是背景方便进行维护的,因为每个模块之间不存在强烈的耦合关系,且互不影响,因此当我们需要修改某个模块时,不需要关注该模块以外的东西。

常用的命令:

引入:

import '路径'

import x from '路径'

导出:

export default x