MVC
MVC是一种设计模式,设计模式是对通用代码的别称。
- M(Model)——数据模型,负责操作所有的数据。
// 以伪代码示例
const m = {
data:{n : parseInt(localStorage.getItem('n'))},//程序中涉及需要的数据
create(){ 增数据 },
delete() { 删数据 },
update(x) { //更新数据
m.data.n = x
eventBus.trigger('m:updated')
localStorage.setItem('n',m.data.n)
},
get(){获得数据}
}
- V(View)—— 视图,负责所有UI页面。需要html、render等。
所有的视图都是数据的渲染(view = render(data),但是render渲染很粗犷)。
//以下列伪代码为例
const v = {
el:需要渲染的容器,
html1:`页面内容`,
render(data){渲染页面}
}
- C(Controller)——控制器,定义用户界面对用户输入的响应方式,起到不同层面间的组织作用,用于控制应用程序的流程,处理用户的行为和数据model上的改变。
//伪代码示例
const c = {
init(container){ //c的初始化
v.init(container)//给v传入了容器也进行初始化
v.render(m.data.n) //view = render(data)
eventBus.on('m:updated',()=>{ //监听到数据变化后重新渲染
v.render(m.data.n)
}
},
events:{事件以哈希表记录},
add(){m.data.n+=1},
minus(){m.data.n -=1},
mul(){m.data.n *=2},
div(){m.data.n /=2},
autoBindEvents(){自动绑定事件}
}
MVC中三部分的代码是有交集的,有时候也可以合并编写,不是一定要分成M、V、C三个部分。
EventBus
EventBus 可以实现对象之间的通信,如果当数据或某些特性发生改变时触发事件,另一个地方监听事件那就可以做到通信了。 常用的API有:
- on——监听事件;
- trigger——触发事件;
- off——取消监听
示例:
const eventBus = $(window)
eventBus.trigger('pikapika')//触发事件,事件名为pikapika
eventBus.on('pikapika',()=>{ //监听到事件pikapika,打印内容
console.log('去吧皮卡丘')
})
eventBus.off('pikapika')//取消监听此事件
EventBus的封装
EventBus 可以使代码更加格式化,尤其在模块化的操作中,封装 + 继承这些就非常重要。 下面是对EventBus的封装:
import $ from 'jquery'
class EventBus{
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 EventBus
class EventBus的继承
可以使class Model继承 class EventBus,但是子类constructor里要调用 super 方法,因为子类自己的 this 对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其加上子类自己的实例属性和方法。
import EventBus from './EventBus.js'
class Model extends EventBus{
constructor(options){
super(); // super 关键字,它在这里表示父类的构造函数,用来新建父类的 this 对象
......
表驱动编程
表驱动编程是从大量相似的代码中抽取出本质的东西,组成哈希表,利用表进行编程,以减少重复代码。表驱动编程的意义在于逻辑与数据的分离。
原来的加减乘除事件:
v.on('click','#add1',()=>{
m.data.n+=1;
})
v.on('click','#minus1',()=>{
m.data.n-=1;
})
v.on('click','#mul2',()=>{
m.data.n*=2;
})
v.on('click','#divide2',()=>{
m.data.n/=2;
})
将其中的关键信息编入哈希表中:
const c ={
events:{
'click #add1': 'add',
'click #minus1': 'minus',
'click #mul2': 'mul',
'click #divide2': 'div'
},
add(){m.data.n+=1},
minus(){m.data.n -=1},
mul(){m.data.n *=2},
div(){m.data.n /=2},
};
执行哈希表中的事件:
autoBindEvents(){
for(let key in c.events){
const spaceIndex = key.indexOf(' ')
const part1 = key.slice(0,spaceIndex)
const part2 = key.slice(spaceIndex + 1) // 因为不能包含空格所以下标+1
const value = c[c.events[key]]
// c.events[key]——字符串,c[c.events[key]]——对应的操作
v.on(part1, part2, value)
}
}
表驱动编程提高了程序的可读性,减少了重复代码,也更容易扩展内容。
关于模块化
从业务角度来看,一个项目就是由各个模块组合而成,如果新开发的项目可以复用原先的模块,就可以减少工作量,也有利于代码维护,出现了错误也可以找到对应的模块里去修改。
按照这个思路,开发新项目时,开发人员的工作从原来的单纯的开发功能变为:接入已有的功能模块,开发不存在的功能模块。但是开发模块也有要求:
- 模块独立可运行;
- 模块可满足扩展的需求。 为了让不同的项目去实现各自的需求,又不重复开发通用代码,模块需要做到独立,互不影响,代码结构清晰。