什么是MVC
MVC包括三个对象,分别是Model、View、Controller,通过将代码进行模块化的分离,以便于提高代码的灵活性和复用性。
- M-model数据模型,负责与数据相关的任务(包括增删改查),会有一个或多个视图监听该模型,一旦数据发生改变,模型将通知相关视图
- V-view视图层,是提供给用户操作的界面,当model层的数据发生改变,视图也会相应的进行更新
- C-controller控制器,起到在不同层面的组织作用。通过监听用户事件,来处理用户行为和数据上的改变
Model层:负责操作数据
1 | let Model = { |
View层:负责视图
1 | let View = { |
Controller层:负责其他
1 | let Controller = { |
EventBus
EventBus用于模块间通讯,常用的api包括事件触发(trigger)、事件监听(on)、取消监听(off)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25import $ from 'jquery'
class EventBus {
constructor() {
this._eventBus = $(window)
}
on(eventName,fn){
return this._eventBus.on(eventName,fn)
}
trigger(eventName,data){
return this._eventBus.trigger(eventName,data)
}
off(){
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()
表驱动编程
表驱动是一种编程模式,从哈希表里查找信息而不是重复使用if…else语句。对于简单的情况,使用逻辑语句更加轻松直白,但对于复杂的逻辑链来说,表驱动有着更稳定的复杂度。下面这段代码有着极高的相似度和重复性:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27$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.on('click',()=>{
let n = parseInt($number.text())
n *= 2
localStorage.setItem('n',n)
$number.text(n)
})
$button4.on('click',()=>{
let n = parseInt($number.text())
n /= 2
localStorage.setItem('n',n)
$number.text(n)
})
用事件委托1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25const c = {
init(container) {
v.init(container)
v.render(m.data.n)
c.bindEvents()
}
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)
})
}
}
用哈希表存储<按钮>及按下按钮对应的<事件>,这样使得数据和事件清晰的分离1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29const c = {
events:{
'click #add1':'add',
'click #minus1':'minus',
'click #mul2':'mul',
'click #divide2':'div'
},
add(){
m.update( data: {n:m.data.n + 1})
},
minus(){
m.update( data:{n:m.data.n - 1})
},
mul(){
m.update( data: {n:m.data.n * 2})
},
div(){
m.update(data: {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)
}
}
}
模块化
- 将一段复杂的代码依据一定的规则抽取成一个个较小规模的块,块与块之间相对独立
- “块”可以理解为实现特定功能的一组方法
- 块的内部数据(变量)与实现(函数)是私有的,只是向外部暴露了一些接口使其与外部其它模块通讯
- ES6语法中引入import和export来导入和导出模块
1
2
3
4
5
6
7// 导出
export default c // 默认导出方式
export {name, draw, reportArea, reportPerimeter} // 导出所有想要导出的模块,使用花括号并用逗号分隔
// 导入
import xxx from './app1.js'
import {name, draw, reportArea, reportPerimeter} from '/js-examples/modules/basic-modules/modules/square.mjs'
事不过三抽象思维
- 同样的代码写三遍,就应该抽成一个函数
- 同样的属性写的三遍,就应该做成共用属性(原型或类)
- 同样的原型写三遍,就应该用继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21class Model{
constructor(options){
['data','update','create','get'].forEach(key)=>{
if(key in options){
this[key] = options[key]
}
}
}
create(){
console && console.error && console.error('还没实现 create')
}
delete(){
console && console.error && console.error('还没实现 delete')
}
update(){
console && console.error && console.error('还没实现 update')
}
get(){
console && console.error && console.error('还没实现 get')
}
}