Model(模型)View(视图)Controller(控制)
用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。
这种架构设计模式,理论上无论程序有多么的复杂,都可以从结构上分成这三类对象。
1.Model(模型),用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法,会有一个或多个视图监听此模型。一旦模型的数据发生变化,模型将通知有关的视图
// 伪代码示例
Model = {
data: { 程序需要操作的数据或信息 },
create: { 增数据 },
delete: { 删数据 },
update(data) {
Object.assign(m.data, data) //使用新数据替换旧数据
eventBus.trigger('m:upate') // eventBus触发'm:update'信息, 通知View刷新
},
get:{ 获取数据 }
}
2.视图(View),是屏幕上的表示,描绘的是model的当前状态。当模型的数据发生变化,视图相应地得到刷新自己的机会
// 伪代码示例
View = {
el: 需要刷新的元素,
html: `<h1>M V C</h1>....显示在页面上的内容`
init(){
v.el: 需要刷新的元素
},
render(){ 刷新页面,渲染 }
}
3.控制器(Controller),定义用户界面对用户输入的响应方式,起到不同层面间的组织作用,用于控制应用程序的流程,它处理用户的行为和数据model上的改变。
/ 伪代码示例
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
模型、视图与控制器的分离,使得一个模型可以具有多个显示视图。如果用户通过某个视图的控制器改变了模型的数据,所有其它依赖于这些数据的视图都应反映到这些变化。因此,无论何时发生了何种数据变化,控制器都会将变化通知所有的视图,导致显示的更新。这实际上是一种模型的变化-传播机制。模型、视图、控制器三者之间的关系和各自的主要功能
- (1)最上面的一层,是直接面向最终用户的"视图层"(View)。它是提供给用户的操作界面,是程序的外壳。
- (2)最底下的一层,是核心的"数据层"(Model),也就是程序需要操作的数据或信息。
- (3)中间的一层,就是"控制层"(Controller),它负责根据用户从"视图层"输入的指令,选取"数据层"中的数据,然后对其进行相应的操作,产生最终结果。
这三层是紧密联系在一起的,但又是互相独立的,每一层内部的变化不影响其他层。每一层都对外提供接口(Interface),供上面一层调用。这样一来,项目开发就可以实现模块化,修改外观或者变更数据都不用修改其他层,大大方便了维护和升级。
MVC的不足之处:
(1)增加了系统结构和实现的复杂性。对于简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率。
(2)视图与控制器间的过于紧密的连接。视图与控制器是相互分离,但确实联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。
(3)视图对模型数据的低效率访问。依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。
PS:如今的前端的框架(Vue,React)已经慢慢的发展成" MV* "的框架,不断的吸收好的思想进行杂糅和改进,但是MVC的设计思想在他们的前进道路上影响深远,有着浓墨重彩的一笔。
EventBus的作用
- 模块通信
解决模块之间通信的问题,view组件层面,父子组件、兄弟组件通信都可以使用eventbus处理
- 模块解耦
storage change事件,cookie change事件,view组件的事件等,全部转换
使用Event Bus来订阅和发布,这样就统一了整个应用不同模块之间的通信接口问题。
- 父子页面通信
window.postMessage + Event Bus
EventBus的一些常用api
- on(监听事件)
- trigger(触发事件)
- off(取消监听)
eventBus.trigger('m:updated') // 触发事件,大叫'm 已经更新了'
eventBus.on('m:updated', () => { console.log('here') }) '监听事件,听到后执行函数'
表驱动编程
表驱动法是一种编程模式,从表(哈希表)里面查找信息而不是使用逻辑语句(if…else…switch),当是很简单的情况时,用逻辑语句很简单,但如果逻辑很复杂,再使用逻辑语句就很麻烦了。
//代码示例
events:{ // 事件集合的哈希表
'click #app1': 'a操作',
'click #app2': 'b操作',
'click #app3': 'c操作',
'click #app4': 'd操作',
}
autoBindEvents() { // 通过哈希变自动绑定事件
for ( let key in c.events) {
if ( c.events.hasOwnProperty(key) ) {
const spaceIndex = key.indexOf(' ') // 找到'click #app1'空格的数组下标
const part1 = key.slice(0, spaceIndex) // 通过spaceIndex 分割 'click' 和 '#app1'
const part2 = key.slice(spaceIndex + 1)
const value = c[c.events[key]]
v.el.on(part1, part2, value) // 自动把多个事件绑定在一个元素上
}
}
}
// 如何一来在稳定的复杂度里,可以绑定多个事件
对模块化的理解
对于前端而言,模块化是指将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起,并且块的内部数据与实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信。
每个模块的实现方式和使用的技术等等都不相同,引入模块化,可以切断每个模块的相互影响,使得单个模块中可以更好的优化代码。
模块化可以降低代码耦合度,减少重复代码,提高代码重用性,并且在项目结构上更加清晰,便于维护。
参见文章