引用了 前端MVC变形记得部分内容 作者:wangfengjiao
MVC是什么?
首先都听说过这个词MVC,好像每个人讲的都不太一样,因为MVC是没有严格的定义的.
只有他们所代表的(Mdel-view-controller)是明确的.那么我们就围绕这个来说一说
MVC模式(Model-view-controller)是一种设计模式(软件架构)
MVC包括三类对象,将它们分离以提高灵活性和复用性。
- 模型(Model) : 同于数据管理, 一旦模型的数据发生改变,Model将通知有关的视图。
- 视图(View) : 负责用户界面,HTML渲染。 描绘的是Model的当前状态,当模型的数据发生改变,View就会刷新自己。
- 控制(Controller) : 控制器, Controll 控制其他所有流程。 负责监听并处理视图(View)的事件。更新和调用Model。也负责监听Model的变化,并更新View。
其中涉及两种设计模式:
view和model之间的观察者模式,view观察model,事先在此model上注册,以便view可以了解在数据model上发生的改变。
view和controller之间的策略模式
一个策略是一个表述算法的对象,MVC允许在不改变视图外观的情况下改变视图对用户输入的响应方式。例如,你可能希望改变视图对键盘的响应方式,或希望使用弹出菜单而不是原来的命令键方式。MVC将响应机制封装在controller对象中。存在着一个controller的类层次结构,使得可以方便地对原有的controller做适当改变而创建新的controller。
view使用controller子类的实例来实现一个特定的响应策略。要实现不同的响应的策略只要用不同种类的controller实例替换即可。甚至可以在运行时刻通过改变view的controller来改变用户输入的响应方式。例如,一个view可以被禁止接受任何输入,只需给他一个忽略输入事件的controller。
JavaScript中的MVC
上面是经典的MVC,现在讲JavaScript的MVC的实现
如图所示,view承接了部分controller的功能,负责处理用户输入,但是不必了解下一步做什么。它依赖于一个controller为她做决定或处理用户事件。事实上,前端的view已经具备了独立处理用户事件的能力,如果每个事件都要流经controller,势必增加复杂性。同时,view也可以委托controller处理model的更改。model数据变化后通知view进行更新,显示给用户。这个过程是一个圆,一个循环的过程。
这种从经典MVC到Javascript MVC的1对1转化,导致控制器的角色有点尴尬。MVC这样的结构的正确性在于,任何界面都需要面对一个用户,而controller “是用户和系统之间的链接”。在经典MVC中,controller要做的事情多数是派发用户输入给不同的view,并且在必要的时候从view中获取用户输入来更改model,而Web以及绝大多数现在的UI系统中,controller的职责已经被系统实现了。由于某种原因,控制器和视图的分界线越来越模糊,也有认为,view启动了action理论上应该把view归属于controller。比如在Backbone中,Backbone.View和Backbone.Router一起承担了controller的责任。这就为MVC中controller的衍变埋下了伏笔。
//数据Model
const model = {
// 数据
data: {},
// 对数据处理的一些方法
create() {},
delete() {},
update() {},
get() {}
}
//视图View
const view = {
//1、一个空容器,以后就是装html的容器
el: null,
//2、要添加的html
html: `<div>123</div>....`,
//3、初始化容器函数,参数是我们给的要当容器的元素(应该是index.html里就有的元素)
init(container) {v.el = $(container);},
//4、渲染函数,参数将是数据。也就是视图全都是对数据渲染 view = render(data)
render(x) {}
}
// 控制Controller
// 处理数据的事件,并把结果渲染到视图View上
const c = {
//1.总初始化函数。
init(container) {},
// 事件
events: {},
//事件要执行的函数
add() {},
minus() {},
mul() {},
div() {},
}
EventBus
EventBus能干嘛?
- EventBus 主要用于对象间的通信,
- 使用 EventBus 可以满足最小知识原则,model和view互相不知道对方的细节,但是却可用调用对方的功能。
怎么实现?
- 比如用vue, new一个新的vue,它是一个实例对象。
- 但是最重要的在于它原型上有我们用到的on(监听)、on(监听)、off(解绑)、$emit(触发)等API 。
const m = {
....
update(data) {
Object.assign(m.data, data)
eventBus.trigger('m:updated') // 通知一下view层,我已经更新了数据,view该开始工作了
localStorage.setItem('n', m.data.n)
},
....
}
然后在controller,controller会用 on 监听事件, 然后通知 view 模型去重新渲染页面
const c = {
init(container) {
v.init(container)
v.render(m.data.n) // view = render(data)
c.autoBindEvents()
eventBus.on('m:updated', () => { // controller会用 on 监听事件,
//然后通知 view 模型去重新渲染页面
console.log('here')
v.render(m.data.n)
})
},
...
}
表驱动编程
当我们需要判断 3 种以上的情况,做出相应的事情,往往需要写很多很多的 if else,这样的代码可读性不强。 为了增强代码的可读性,我们可以用表驱动编程,把用来做 if 条件判断的值存进一个哈希表,然后从表里取值。 而这种做法的意义就在于: 逻辑和数据是分离的
// 伪代码
function contry(国家名){
if(中国){
return "CHN"
}else if(日本){
return "JPN"
}else if(美国){
return "USA"
}else{
return "OTHER"
}
}
用 if else语句这样做,如果我再增加一个国家,那么就要写一个if else语句。等于又增加了一条逻辑。
那么我们为何不用 表数据编程 把 数据和逻辑分离开实现呢。 数据添加是简单的,而逻辑的添加是高成本,高风险的
表驱动编程做法:
伪代码
function contry(国家名){
const 国家列表 = [
"中国" = "CHN"
"日本" = "JPN"
"美国" = "USA"
]
国家简写转换:funciton(){
for(let 国家名 in 国家列表){
return 国家列表[国家名] // 返回的就是 国家简写
}
}
}
我理解的模块化
化繁为简,灵活多变,解耦
将一个复杂的程序依据一定的规则(规范)封装成几个块(文件)并进行组合。
模块的内部数据的实现是私有的,只是向外部暴露一些接口(方法)与外部其他模块通信。这则就是模块化。
模块化的好处:
- 降低代码耦合度
- 减少重复代码
- 提高代码重用性
- 在项目结构上更加清晰,便于维护。