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:
- on:监听自定义事件,绑定事件响应函数;
- trigger:在某个地方触发自定义事件。
enentBus的函数一般有:
- on(事件的绑定)
// 事件列表对象
const evetList = {}
function on(eventName,callback) {
if(!(eventName in eventList)) {
// 事件没有记录过,则为这个事件创建一个新的通知列表
eventList[EventName] = []
}
// 将响应函数callback放到eventName的通知列表中,等待被触发
eventList[EventName].push(callback)
}
- emit/trigger(事件的触发)
const eventList = {}
const trigger = (eventName,params)=>{
// 找到与eventName对应的通知列表,将params传给通知列表内的每个响应函数
if(eventList[eventName]){
let arr = eventList[eventName];
arr.forEach(callback => {
callback(params)
})
}
}
- off(事件的解绑)
const eventList = {}
const off = (eventName,callback) => {
if(eventList[eventName]) {
let index = eventList[eventName].indexof(callback)
eventList[eventName].splice(index,1)
}
}
表驱动编程
先说结论:表驱动编程是一种编程方法,用来消除代码中频繁的if else
或switch 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)
})
表驱动编程的好处
- 减少代码冗余;
- 数据与逻辑分离,数据的增加不会造成逻辑代码的增加
- 方便维护代码
模块化
为什么要模块化
- 可以避免命名冲突
- 灵活架构,每个模块完成一个特定的子功能,所有的模块按某种方法组装起来,成为一个整体,完成整个系统所要求的功能。
- 提高复用性和可维护性
比如说将代码重构为mvc式,m、v、c也是模块,首先模块化可以让文件更有组织,哪个模块出了问题就去哪个模块解决问题,其次是用模块的时候我们不用关心模块内的具体实现,使用模块暴露出的接口就可以了。
模块化的规范
- RequireJS模块:RequireJS遵循的是AMD规范。AMD规范是异步加载,依赖前置,特点是准备充分,但加载会较慢。使用define()定义模块,require()加载模块。
- SeaJS模块:SeaJS遵循的是CMD规范。CMD规范也会异步加载,不同的是CMD依赖就近,特点是首次加载很快。CMD规范也是使用define()定义模块,require()加载模块。但和AMD规范思想不同,写法也不同。
- CommonJS:CommonJS规范使用require加载模块,module.exports导出模块,module可省略,但不推荐。特点是加载模块顺序按照词法解析的顺序加载,是同步加载的。
- ES6模块:ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系。使用import和export来导入导出,ES6还提供了一个default,用来提供默认的export。特点是加载模块存储的是值的引用,所以全局只有一份;加载模块也是异步的。ES6的module吸收了CommoneJS和AMD两者的优点,兼容两标准的规范。