一、MVC是什么
M:模型(Model):数据保存
V:视图(View):用户界面。
C:控制器(Controller):业务逻辑
一个软件的实现可以分成3个部分,他们相互之间可以通信,根据他们的通信方式可以衍生出其他的几种设计模式
传统意义上的MVC各个部分之间的通信是单向的
-
View 传送指令到 Controller
-
Controller 完成业务逻辑后,要求 Model 改变状态
-
Model 将新的数据发送到 View,用户得到反馈
各个部分的伪代码实现
Model
Model = {
data: { 程序需要操作的数据或信息 },
create: { 增数据 },
delete: { 删数据 },
update(data) {
Object.assign(m.data, data) //使用新数据替换旧数据
eventBus.trigger('m:upate') // eventBus触发'm:update'信息, 通知View刷新
},
get:{ 获取数据 }
}
View
View = {
el: 需要刷新的元素,
html: `<h1>M V C</h1>....显示在页面上的内容`
init(){
v.el: 需要刷新的元素
},
render(){ 刷新页面 }
}
Controller
Controller = {
init(){
v.init() // View初始化
v.render() // 第一次渲染
c.autoBindEvents() // 自动的事件绑定
eventBus.on('m:update', () => { v.render() }) // 当eventBus触发'm:update'时View刷新
},
events:{ 事件以哈希表方式记录 },
method() {
data = 改变后的新数据
m.update(data)
},
autoBindEvents() { 自动绑定事件 }
}EventBus
MVP
MVP 模式将 Controller 改名为 Presenter,同时改变了通信方向。
其中model和view之间不再通信,而是通过presenter这个中介来实现交互,此时代码大部分分布在presenter上
MVVM
MVVM 模式将 Presenter 改名为 ViewModel,基本上与 MVP 没有区别。
唯一的区别是,它采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然。Angular 和 Ember 都采用这种模式。
二、eventBus
DOM 的事件机制就是发布订阅模式最常见的实现,这大概是前端最常用的编程模型了,监听某事件,当该事件发生时,监听该事件的监听函数被调用。eventBus就是实现发布订阅模式的一种方法。
这里可以看下发布订阅和观察者模式的小区别
其实jquery和vue构造出来的对象都有监听事件的方法,我们只需要构造一个对象,然后调用他的on 和 trigger方法就可以实现组件之间的通信
下面介绍几个vue常用的事件api及其内部实现
1.$on 事件的订阅
$on(eventName,callback)
//参数1:事件名称 参数2:事件函数
//判断当前事件名称是否存在,如果不存在则创建一个key值为事件名称
//value为一个数组 将callback push到数组中
const eventList = {};
const $on = (eventName,callback)=>{
if(!eventList[eventName]){
eventList[eventName] = [];
}
eventList[eventName].push(callback)
}
2.$off 事件的解绑
$off(eventName,[callback])
//参数1:事件名称 参数2:[事件函数]
//判断当前事件名称是否存在,如果存在继续判断第二个参数是否存在,如果存在则找到相//对应的下标 然后将函数在数组中移除
//如果不存在则将整个数组清空
const eventList = {};
const $off = (eventName,callback)=>{
if(eventList[eventName]){
if(callback){
let index = eventList[eventName].indexOf(callback);
eventList[eventName].splice(index,1)
}
}else{
eventList[eventName].length = 0;
}
}
export default = {
$on,
$emit,
$off
}
3.$emit 事件的触发
$emit(eventName,[params])
//参数1:事件名称 参数2:[需要传递的参数]
//判断当前事件的名称是否存在,如果存在则遍历数组,得到所有的函数,
//并执行。然后将params当做实参传递到函数中去
const eventList = {};
const $emit = (eventName,params)=>{
if(eventList[eventName]){
let arr = eventList[eventName];
arr.map((cb)=>{
cb(params)
})
}
}
三、表驱动编程
我们先看一个简单例子来感受下表驱动,由于看了很多博客都是非要整的很复杂的代码,这样让人的关注点都在理解代码本身上了,所以我自己写了个很简单的例子
function age(name){
if(name==="小明"){
console.log("年龄是"+10)
}else if(name==="小白"){
console.log("年龄是"+14)
}else if(){
}
//....等等
}
假如我们要写一个查询年龄的函数,如果用if else语句写,那么有多少人我们就得写多少个if else语句,这样我们的代码量是线性增长的,如果我们用一个哈希表来记录这些映射关系会怎样
const list={
"小明":10,
"小白":14,
//等等...
}
function age2(name){
if(name in list){
console.log(name+"的年龄是"+list[name])
}else{
console.log("查无此人")
}
}
这样我们的数据部分就和代码部分分离了,函数主体只是去表中查找然后输出结果,这就是表编程的基本思想,在我看来这就是编程的基本思想,从我们发明计算机的那一刻就是为了能减少重复的工作,正所谓懒才是人类进化的源驱动力。。。。
其实这里我还行扯一下《西部世界》中的一个钢琴,“密尔顿自动演奏钢琴”
现在感觉这个跟表编程的概念好吻合的,我只是给你一个乐谱,实现的函数(钢琴)不会发生改变,而你可以随意的自定义乐谱。
四、我眼中的模块化编程
其实现在的开发所形成出的模块化也是一种懒的体现,为了实现代码的可复用性,便于管理以及可移植性,以及防止作用域冲突等,模块化是历史的必然,因为总有人会厌倦日复一日的重复相同的东西,所以就衍生出了MVC,把一个页面上的不同部分的功能分离,把一个功能的数据,视图,控制分离,都只是为了“偷懒”。
总结一点技术性的模块化知识吧
前流行的模块化规范有CommonJS
,AMD
,CMD
,ES6
的import/export
- CommonJS的主要实践者就是nodejs,一般对模块输出用module.exports去输出,用require去引入模块,CommonJS一般采用同步加载【require / module.exports / exports】
- AMD遵从RequireJs规范,推崇依赖前置(提前执行)【require / defined】
- CMD遵从SeaJs规范,推崇依赖就近(延迟执行)【require / defined】
- ES6 可静态分析,提前编译,不是在运行时确认【import / export】