浅析MVC

164 阅读3分钟

1.MVC 是什么?

答:MVC 是设计模式的一种。

2.设计模式是什么?

答:设计模式就是一段通用的代码。比如:封装了一个公用的工具函数,可以引入在不同的项目中。这种就是一种设计模式。

3.为什么会有设计模式?

答:因为我们在工作中需要用到大量的重复代码,我们需要把重复的代码或者业务单独提取出来,那么这样的操作就是设计模式。而 MVC 就是一种非常重要的设计模式。

3.编程中什么是重复的?

答:相同的三行代码出现了两遍。解决方法,提取公共函数。相同的页面写了10遍,这时候就应该想出一个 万金油 的方法,MVC 就是这样的 万金油 ,任何页面都能采用 MVC 来进行重构。

mvc

MVC 每个模块都可以分成三个对象,分别是 M 层、V 层、C 层。

  • M 指的是 Model(数据模型)负责操作所有的数据。
  • V 指的是 View(视图层)负责展示视图。
  • C 值的是 Controller(控制层)负责其他操作。比如:初始化方法、对数据进行增删改查的方法。

代码

const m = {
  data: {
    index: 0,
    n: 0
  }
}

const v = {
  template: `
    <div>
      <span>姓名</span>
      <input type="text">
    </div>
  `
}

const c = {
  init() {
    // 初始化数据操作
  }
}

模块化

代码采用模块化后,可以做到互不影响 A 模块的改变并不会影响 B 模块的使用,这样可以使得我们的程序更加清晰和容易维护。

JavaScript 主要通过 import(导入) 与 export(导出) 来进行模块化操作,如:

// 文件名name.js
const name = 'Jacky'
export {
    name
}
// index.js
import { name } from './name.js'
console.log(name) // "jacky"

关于 export 与 import 的介绍可以到 MDN 进行查看它们的语法。MDN export MDN import

注意:如果我们想在 JS 中使用模块化,首先需要 而 import 与 export 只能在模块内部使用。

什么是eventBus

eventBus 的作用主要是用来实现对象之间的通信,例如我们可以通过 eventBus 来实现 M、V、C 三个对象之间的通信。

eventBus 有三个主要的方法:

  • on 用于监听对象。
  • trigger 用于触发对象。
  • off 用于取消对象监听。

在 MVC 中我们想实现 M 层数据的变化能自动触发 V 层的更新,这时候就可以采用 eventBus 的方法。

const eventBus = $(window) // 获取eventBus 对象
const m = {
  data: {
    ...
  },
  update(data) {
    // trigger 触发事件更新数据
    eventBus.trigger('m:updated')
  }
}

const v = {
  render(data) {
    ...更新数据
  }
}  
  
const c = {
  eventBus.on('m:updated', () => {
    // on方法执行监听后触发 v 的视图更新
    v.render(m.data.n)
  })
}

什么是表驱动编程

这里的表指的是 哈希表 ,这是一种很重要的编程思想。它的核心就在于通过哈希表的形式把重复的代码进行剥离。如下代码:

// jQuery 风格写法
$('#el1').on('事件A', fn1)
$('#el2').on('事件B', fn2)
$('#el3').on('事件C', fn3)
$('#el4').on('事件D', fn4)
$('#el5').on('事件E', fn5)

上述代码的共同点,在于都是在做同一件事情。通过一个元素("#el1等")执行某个事件("事件A等")从而达到某个目的("fn1等")。那么这样的代码是可以被优化为表结构的。如下:

const evevts = {
  "#el1 事件A": "fn1",
  "#el2 事件B": "fn2",
  "#el3 事件C": "fn3",
  "#el4 事件D": "fn4",
  "#el5 事件E": "fn5"
}

const eventFunctions = {
  fn1: function() {},
  fn2: function() {},
  fn3: function() {},
  fn4: function() {},
  fn5: function() {}
}

// 通过一个公共的函数即可实现所有事件的调用
function autoBindEvents() {
  for(let key in events) {
    const spaceIndex = key.indexOf(' ')
    const el = key.splice(0, spaceIndex) // 获取元素
    const event = key.splice(spaceIndex+1) // 获取元素需要执行的事件
    const fn = eventFunctions[events[key]] // 执行事件后的函数
    $(el).on(event, fn)
  }
}

从优化的代码来看,我们可能觉得是不是更加复杂了。但是随着元素的增多,事件的增多后,我们只需要维护哈希表就行而不必再管逻辑的执行,因为无论是一个元素还是一万个元素甚至更多的元素都是同样的写法。所以,如果我们使用表驱动编程可以带来很多的优点:

  • 减少了重复的代码。
  • 增加了可扩展性,如:我们想为 #el2 换一个事件时执行操作 events 即可。
  • 降低逻辑复杂度,把需要 DOM 操作的地方变换为对一个数据表格的操作。