经典MVC模式中,M是指业务模型,V是指用户界面,C则是控制器,使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。其中,View的定义比较清晰,就是用户界面。
1.MVC的三层结构
1.1 M
M即model模型,是指模型表示业务规则(也就是程序需要操作的数据或信息)。在MVC的三个部件中,模型拥有最多的处理任务。被模型返回的数据是中立的,模型与数据格式无关,这样一个模型能为多个视图提供数据,由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性。
JS代码示例
const m = { // 模拟数据模型
data: { // 使用的数据
n: parseInt(localStorage.getItem('n'))
},
create() {},
delete() {},
update(data) { // 修改数据
Object.assign(m.data, data)
eventBus.trigger('m:updated')
localStorage.setItem('n', m.data.n)
},
get() {}
}
1.2 V
V即View视图,是指用户看到并与之交互的界面。比如由html元素组成的网页界面,或者软件的客户端界面。MVC的好处之一在于它能为应用程序处理很多不同的视图。当模型的数据发生变化,视图相应地刷新自己展示出来的页面。
JS代码示例
const v = { // 视图相关代码
el: null,
html: `
<div>
<div class="output">
<span id="number">{{n}}</span>
</div>
<div class="actions">
<button id="add1">+1</button>
<button id="minus1">-1</button>
<button id="mul2">*2</button>
<button id="divide2">÷2</button>
</div>
</div>
`,
init(container) {
v.el = $(container)
},
render(n) {
if (v.el.children.length !== 0) v.el.empty()
$(v.html.replace('{{n}}', n))
.appendTo(v.el)
}
}
1.3 C
C即controller控制器,是指控制器接受用户的输入并调用模型和视图去完成用户的需求,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。
JS代码示例
const c = { // 控制器
init(container) {
v.init(container)
v.render(m.data.n) // view = render(data)
c.autoBindEvents()
eventBus.on('m:updated', () => {
console.log('here')
v.render(m.data.n)
})
},
events: {
'click #add1': 'add',
'click #minus1': 'minus',
'click #mul2': 'mul',
'click #divide2': 'div',
},
add() {
m.update({n: m.data.n + 1})
},
div() {
m.update({n: m.data.n / 2})
},
autoBindEvents() {
for (let key in c.events) {
const value = c[c.events[key]]
const spaceIndex = key.indexOf(' ')
const part1 = key.slice(0, spaceIndex)
const part2 = key.slice(spaceIndex + 1)
v.el.on(part1, part2, value)
}
}
}
三者关系示意图
![](https://img.haomeiwen.com/i25448917/f88a3acf2541107d.png)
Controller通过调用相应的方法改变Model数据模型中的数据等,数据模型Model的改变时,使视图随之变化,视图由于用户的点击触发又会通过Controller去改变数据模型Model,这是一个循环的过程。
2. EventBus
当两个组件之间毫无关联,或者他们之间结构复杂,但此时需要传递数据,就需要用到件总线 EventBus的概念。
EventBus能够简化各组件间的通信,让我们的代码书写变得简单,能有效的分离事件发送方和接收方(也就是解耦的意思),能避免复杂和容易出错的依赖性和生命周期问题。
EventBus基本的api有 on(监听事件),trigger(emit)(触发事件),off(取消监听)方法。用于模块间的通讯,view组件层面,父子组件、兄弟组件通信都可以使用eventbus 处理。
JS代码示例
//EventBus.js
class EventBus {
constructor() {
this._eventBus = $(window)
}
on(eventName, fn) {
return this._eventBus.on(eventName, fn)
}
trigger(eventName, data) {
return this._trigger.trigger(eventName, data)
}
off(eventName, fn) {
return this._eventBus.off(eventName, fn)
}
}
export default EventBus
//new.js
import EventBus from 'EventBus.js'
const e = new EventBus()
e.on()
e.trigger()
e.off()
3. 表驱动编程
表驱动方法是一种使你可以在表(哈希表)中查找信息,而不必用逻辑语句(if 或 case)来把他们找出来的方法。
表驱动编程的意义在于逻辑与数据的分离。
JS代码示例
bindEvents(){
v.el.on('click', '#add1', () => {
m.data.n += 1
v.render(m.data.n)
})
v.el.on('click', '#minus1', () => {
m.data.n -= 1
v.render(m.data.n)
})
v.el.on('click', '#mul2', () => {
m.data.n *= 2
v.render(m.data.n)
})
v.el.on('click', '#divide2', () => {
m.data.n /= 2
v.render(m.data.n)
})
}
将其中重复部分单独使用,相同参数的不同变量提取出来合成一个哈希表,使逻辑与数据分离。
events: {
"click #add1": "add",
"click #minus1": "minus",
"click #mul2": "mul",
"click #divide2": "divide",
"click #reset": "reset",
},
add() {
m.update({ n: m.data.n + 1 })
},
minus() {
m.update({ n: m.data.n - 1 })
},
mul() {
m.update({ n: m.data.n * 2 })
},
divide() {
m.update({ n: m.data.n / 2 })
},
autoBindEvents() {
for (let key in c.events) {
const value = c[c.events[key]]
const spliceIndex = key.indexOf(" ")
const part1 = key.slice(0, spliceIndex)
const part2 = key.slice(spliceIndex + 1)
v.el.on(part1, part2, value)
}
},
2. JS模块化语法
模块化开发使代码藕合度降低,模块化的意义在于最大化的设计重用,以最少的模块、零部件,更快速的满足更多的个性化需求。因为有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块。但总不能随便写吧,总得有规范让大家遵守吧。因此ES6出现了模块化
export(导出)、import(引入)命令
export
命令用于规定模块的对外接口。
一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。
export输出变量的写法可以查看cdn文档。
export命令可以出现在模块的任何位置,只要处于模块顶层就可以。如果处于块级作用域内,就会报错,下面的import命令也是如此。
import
命令用于输入其他模块提供的功能。
网友评论