三层架构和mvc能一起使用吗_浅谈MVC

设计模式

在讲述MVC之前,我们先来了解下什么是“设计模式”?

通俗的来讲就是有一个程序员写了一份代码,他觉得自己写的非常好,可以供他人借鉴,于是就对这种写法取一个名字,比如”适配器模式“。“设计模式”就是对通用代码取一个名字而已。

为什么要有设计模式呢?

为了不让你重复你自己(Don't Repeat Yourself - DRY 原则)


什么是MVC?

如何设计一个程序的结构叫做"架构模式"(architectural pattern)。MVC模式就是架构模式的一种,它对我后面框架的学习启发很大。我觉得这种模式也可以应用于其他领域。

下面是我个人对MVC模式的一些个人理解,有的地方不一定真确,仅供思路理解作参考。

提问:你见过面条式代码吗?什么样的代码是面条式的?

61cf09024ad21469dce57a50f6be4cbc.png

比如像下面这种代码:

$("button").prop("disabled", true)
$("textarea").on("input", function() {
  if ($(this).val().length > 0) {
    $("button").prop("disabled", false)
  } else {
    $("button").prop("disabled", true)
  }
})

你如果不学MVC,你将写出面条式的烂代码。不停的重复自己、不懂得抽象思维、只会调API、不懂封装、不会造轮子,不能提升自己!!!

讲了这么多,就是希望你能学习MVC设计模式,对你以后的发展会有更大的帮助。

MVC定义:MVC分别是Model(模型)、View(视图)和Controller(控制)。该模式认为,无论你的程序多么简单或复杂,从结构上看,都可以分成三层。

  • Model(模型):负责操作所有数据
  • View(视图):负责所有 UI 界面
  • Controller(控制):负责其它相应操作

这三层是紧密联系在一起的,但又是互相独立的,每一层内部的变化不影响其他层。每一层都对外提供接口,供上面一层调用。这样一来,软件就可以实现模块化,修改界面或者变更数据都不用修改其他层,大大方便了维护和升级。


下面请看<代码一>→<代码二>的抽离过程

代码一:

//初始化html
const html = `
<div>
    <div class="output">
      <span id="number">{{n}}</span>
    </div>
    <div class="actions">
      <button id="add1">+1</button>
      <button id="minus1">-1</button>
      <button id="mul2">*2</button>
      <button id="divide2">÷2</button>
    </div>
</div>
`
const $element = $(html).appendTo($('body').page)
//获取元素
const $button1 = $('#add1')
const $button2 = $('#minus1')
const $button3 = $('#mul2')
const $button4 = $('#divide2')
const $number = $('#number')
//初始化数据
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.on('click', () => {
    let n = parseInt($number.text())
    n *= 2
    localStorage.setItem('n', n)
    $number.text(n)
})
$button4.on('click', () => {
    let n = parseInt($number.text())
    n /= 2
    localStorage.setItem('n', n)
    $number.text(n)
})

代码二:

const eventBus = $(window)    //拥有.on(监听事件)函数/.trigger(触发事件)函数
// 数据相关都放到M
const m = {
  data: {
    n: parseInt(localStorage.getItem('n'))//初始化数据
  },
  create() {},
  delete() {},
  update(data) {
    Object.assign(m.data, data)
    eventBus.trigger('m:updated')//m跟新了,会触发(事件不能有空格)
    localStorage.setItem('n', m.data.n)
  },
  get() {}
}
// 视图相关都放到V
const v = {
  el: null,
  html: `
  <div>
    <div class="output">
      <span id="number">{{n}}</span>
    </div>
    <div class="actions">
      <button id="add1">+1</button>
      <button id="minus1">-1</button>
      <button id="mul2">*2</button>
      <button id="divide2">÷2</button>
    </div>
  </div>
`,
  init(container) {//外面传入container
    v.el = $(container)
  },
  render(n) {
    if (v.el.children.length !== 0) v.el.empty()
    $(v.html.replace('{{n}}', n))
      .appendTo(v.el)
  }
}
// 其他都C
const c = {
  init(container) {//初始化
    v.init(container)
    v.render(m.data.n) // view = render(data)
    c.autoBindEvents()
    eventBus.on('m:updated', () => {//监听数据变化,然后渲染
      v.render(m.data.n)
    })
  },
  events: {
    'click #add1': 'add',
    'click #minus1': 'minus',
    'click #mul2': 'mul',
    'click #divide2': 'div',
  },
  add() {
    m.update({n: m.data.n + 1})
  },
  minus() {
    m.update({n: m.data.n - 1})
  },
  mul() {
    m.update({n: m.data.n * 2})
  },
  div() {
    m.update({n: m.data.n / 2})
  },
  autoBindEvents() {
    for (let key in c.events) {
      const value = c[c.events[key]]
      const spaceIndex = key.indexOf(' ')
      const part1 = key.slice(0, spaceIndex)
      const part2 = key.slice(spaceIndex + 1)
      v.el.on(part1, part2, value)
    }
  }
}
export default c

从代码一到代码二,你会发现变复杂了,像代码1那样写的多清楚明了,代码2这里那里的,都绕晕了。。。那是你还没有发现MVC的好处。

在代码2中,所有的属性都是经典的、缺一不少的,你可以在任何地方使用(万金油),代码2在代码1的基础上进行了抽象的转化,代码的简化,看起来复杂了,实际上变得简单可复用了。

EventBus 对象间的通信

jQuery中有一个.on()和.trigger()方法,分别用来监听和触发。如果我在一个地方监听事件,一个地方触发事件,那么这两处不是实现了通信吗。 jQuery API具体实现

用法:

  $( "p" ).on( "myEvent", function( event, param ) {
    console.log(param)
  })
  $( "p" ).trigger( "myEvent", [ "123" ] )

下面我们自己实现eventButs(借用了jQuery)

import $ from 'jquery'
class EventButs {
  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 EventButs 

const e = new EventButs ()
e.on()
e.trigger()
e.off()

表驱动编程

定义:当你看到大批类似但不重复的代码时,你需要看看到底哪些才是重要的数据,把重要的数据做成哈希表,你的代码就简单了。

下面的代码可以使用哈希表简化:

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

简化后的:

const c = {
  init(container) {//初始化
    v.init(container)
    v.render(m.data.n) // view = render(data)
    c.autoBindEvents()
    eventBus.on('m:updated', () => {//监听数据变化,然后渲染
      v.render(m.data.n)
    })
  },
  events: {
    'click #add1': 'add',
    'click #minus1': 'minus',
    'click #mul2': 'mul',
    'click #divide2': 'div',
  },
  add() {
    m.update({n: m.data.n + 1})
  },
  minus() {
    m.update({n: m.data.n - 1})
  },
  mul() {
    m.update({n: m.data.n * 2})
  },
  div() {
    m.update({n: m.data.n / 2})
  },
  autoBindEvents() {
    for (let key in c.events) {
      const value = c[c.events[key]]
      const spaceIndex = key.indexOf(' ')
      const part1 = key.slice(0, spaceIndex)
      const part2 = key.slice(spaceIndex + 1)
      v.el.on(part1, part2, value)
    }
  }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值