MVC是啥?
如何设计一个程序的结构,这是一门专门的学问,叫做架构模式(architectural pattern),属于编程的方法论,MVC就是架构模式的一种。
每个模块都可以写成三个对象,分别是M/V/C
M-Model(数据模型)负责操作所有数据
v-view(视图)负责所有UI界面
C-Controller(控制器)负责其他
MVC没有严格的定义,每个程序员对MVC的理解都不一样,但对着三个名字是没有分歧的
mvc伪代码
const m = {//数据层,关于数据的操作放在这里
data:{
n:parseInt(localStorage.getItem('number')||100)
},//数据初始化
update:function (data) {
//更新数据的方法
},
delete:function (data) {
//删除数据的方法
},
get:function (data) {
//获得数据的方法
}
}
const v = {//视图层,关于视图的操作放在这里
el:'挂载点',
html:'需要插入元素内的HTML内容',
render('渲染html')
}
const c = {//控制层,关于事件监听的放到这里
//找到重要的元素绑定事件
//如果触发事件调用更改数据方法及渲染方法
const a =$('找到A')
const b = $('找到B')
b.on('click',function(){
//调用数据层方法更改数据
//调用视图层方法渲染页面
})
}
为什么用MVC
可能对于我们之前用惯写下面这种代码的同学来说用MVC的方式去实现某一个功能会觉得变得更复杂
//找到重要的元素
const $button1 = $("#add1");
const $button2 = $("#minus1");
const $button3 = $("#mul2");
//获得数据
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.......
用上面这些代码写当然没问题,也很简单,想到啥写啥,但如果代码量一上来,我们想找到某个功能的相关代码对其进行修改就会变得非常麻烦,而且还存在变量污染,而且对于有些功能相似的代码,这完全是重复劳动呀,所以复用性低
MVC麻烦是麻烦了点,但是后期维护成本低,因为每一部分代码是以一个对象的方式储存在一个独立的空间,负责某一项功能,我们更容易找到对应代码,并且在其内部修改不会对外部的代码造成很大影响
模块化
上面我们说了MVC解决了随着代码量上升难维护,高重复的等问题
那么模块化就是再此基础上进行了再次的升级,如果随着功能更多,业务逻辑越来越复杂,代码更也会变得更复杂,我们如果用对象的方式去区分某个功能已经不现实了,光变量名都记不过来,显示屏的代码就算折叠起来下拉不超过2页,你估计都头炸,更别说复杂的业务逻辑
所以如果我们的代码有清晰的脉络就会方便很多
就像我们玩的积木一样,各个积木可以组合在一起形成一个形状,又可以拆分,又可以替换,因为基本上各个积木块都是独立的,只要他们之间的接口(形装)匹配,就可以灵活地组合子啊一起,这也叫解耦,当然这是理想的状态,解耦是逐渐达到这个理想的状态。
模块之间的联系越少越好,接口越简单越好(低耦合,细线通信)。如果划分的模块之间接口很复杂,说明功能划分得不太合理,模块之间的耦合太高了,同时也说明某模块的内聚也不高。
EventBus
上面我们说了模块化,既然我们把每个功能都分成不同的模块(文件),那么问题来了。。。如果文件C中检查到用户的操作,需要通知文件M修改数据,M修改了数据需要通知文件V进行页面渲染怎么办?当然是eventbus。。。不管是jquery还是vue中都有类似于eventbus的存在,只不过叫法不一样,不过它们的功能是相似的,都是负责组件(模块)中的通信
eventbus也是一种设计模式或者框架,主要用于组件/对象间通信的优化简化。
下面演示用jquery生成的eventbus
eventbus包含很多方法,on方法可以监听事件,trigger方法可以触发事件,off卸载监听
//伪代码
import $ from 'jquery'
const eventbus = $(window)//返回一个eventbus的所有方法的对象
const model = {//数据层
data:{'数据':1},
update(data){
Object.assign(model.data,data)//更新数据
eventbus.trigger('更新数据')//触发事件
}
}
const View = {
el:'挂载点',
html:'<div>{{内容}}</div>',
init(container){
View.el = $(container)
}
render(data){
$(View.html.replace('{{n}}', n))
.appendTo(View.el)//更换新的内容渲染进页面
}
}
const control = {
init(container){
View.init(container)
View.render(m.data.n)//初始化页面
autoBindEvents()
eventbus.on('更新数据',()=>{//监听数据层的eventbus.trigger如果有被触发说明数据有更新从而进行渲染
View.render(m.data.n)
})
},
add() {
m.update({n: m.data.n + 1})
},//改变数据方法
minus() {
m.update({n: m.data.n - 1})
},//改变数据
//监听改变数据的按钮
autoBindEvents() {
View.el.on('click','app1','add')
}
}
//当然当需求更复杂的时候我们可以将eventbus写成一个类,让生成的实例对象继承eventbus,这样每个对象都拥有了可以触发和监听的功能,相当灵活
eventbus我们可以用第三方的库,也可以自己手写,根据实际情况来决定
表驱动编程
定义:表驱动编程是一种使我们可以再表中查找信息,而不必写很多逻辑语句(if或case)来把它们找出来的方法,事实上,任何信息都可以通过表来挑选,再简单情况下用逻辑语句是更简单的,但是一旦需要判断条件增多那我们可能要写大量重复的判断语句,这时候我们适当的用表中提取出的值来写条件判断将事半功倍。
数组中的应用
假设你需要一个可以返回每个月中天数的函数)
常规写法
对象的应用
假设我们现在要监听某个元素对其绑定事件
我们可能要这么写
document.querySelector('#add1').addEventListener('click', add1)
document.querySelector('#min1').addEventListener('click', min1)
document........
如果监听事件有10个?100个,写的太累了
通过表驱动的方式我们可以写成下面这样
const Controller = {
events: {//表驱动编程,对象
"#add1 click": "add1",//key的前半部分为要监听的元素,后半部分为监听的事件,value为要执行的方法
"#min1 click": "min1"
},
autoBindEvents() {
Object.keys(this.events).forEach(key => {//遍历对象获得对应的值去做赋值操作
const value = this.events[key];
const handler = this[value];
const [selector, event] = key.split(" ");
$(".buttons").on(event, selector, handler);//将提取出来的值去监听事件
});
},
}
总之程序员拒绝重复,程序员追求稳定的简单。
简单总结:写代码就像写作文,好的作品通俗易懂没有废话,条理清晰,落落大方。