1. MVC是什么
- M就是mode,即数据模型,负责数据相关的任务,包括对数据的增刪改查
- V就是view,即视图层,即用户能看得到的界面
- C就是 Controller,控制器,负责监听用户事件,然后调用M和V更新数据和视图
接下来将分别用伪代码表示三个部分的工作内容
1.1 Model数据模型
let Model={
data:{数据源},
create:{增加数据},
delete:{删除数据},
update(data){
Object.assign(m.data,data) // 用新数据替换旧数据
eventBus.trigger('m:update') // eventBus触发'm:update'信息,通知View刷新界面
},
get:{获取数据}
}
复制代码
1.2 View视图层
let View={
el:要刷新的元素,
html:'要显示在页面上的刷新内容'
init(){
v.el:初始化需要刷新的元素
},
render(){
刷新页面
}
}
复制代码
1.3 Controller控制器
控制器通过绑定事件,根据用户的操作,调用M和V更新数据和视图
let Controller={
init(){
v.init() // 初始化View
v.render() // 第一次渲染页面
c.autoBindEvents() // 自动的事件绑定
eventBus.on('m:update',()=>{v.render()} // 当enentsBus触发'm:update'是View刷新
},
events:{事件以哈希表的方式记录存储},
//例如:
events: {
'click #add1': 'add',
'click #minus1': 'minus',
'click #mul2': 'mul',
'click #divide2': 'div',
},
add() {
m.update({n: m.data.n + 1})
},
minus() {
m.update({n: m.data.n - 1})
},
mul() {
m.update({n: m.data.n * 2})
},
div() {
m.update({n: m.data.n / 2})
},
method(){
data=新数据
m.update(data) // controller 通知 model去更新数据
},
autoBindEvents(){
for (let key in c.events) { // 遍历events表,然后自动绑定事件
const value = c[c.events[key]]
const spaceIndex = key.indexOf(' ')
const part1 = key.slice(0, spaceIndex) // 拿到 'click'
const part2 = key.slice(spaceIndex + 1) // 拿到'#add1'
v.el.on(part1, part2, value)
}
}
复制代码
1.4 示例
每次点击相应按钮时,数值发生变化,并在刷新后保留之前的数值,基本思想是监听click事件
MVC操作如下
import './app1.css'
import $ from 'jquery'
const eventBus = $(window)
// 数据相关都放到m
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() {}
}
// 视图相关都放到v
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)
}
}
// 其他都c
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})
},
minus() {
m.update({n: m.data.n - 1})
},
mul() {
m.update({n: m.data.n * 2})
},
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)
}
}
}
export default c
复制代码
在外部引用时
import x from './app1.js'
x.init('#app1') // 把容器div传进去
// 然后在html里写一个
<div id="app1"> </div>
// 作为容器即可
复制代码
2. EventBus
2.1 EventBus 是什么
- EventBus主要用于对象之间的通信,比如在上面的例子中,Model 数据模型 和View 视图模型彼此不知道彼此的存在,但是又需要通信,于是就要用到EventBus
- 总结:使用 eventBus 可以满足最小知识原则,m 和 v 互相不知道对方的细节,但是却可以调用对方的功能
2.2 EventBus提供了哪些API
- EventBus 提供了 on、off 和 trigger 等 API,on 用于监听事件,trigger 用于触发事件
比如在上面的MVC模型中, M数据模型更新时,会 trigger 触发一个事件
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. 表驱动编程
当我们需要判断3种以上的情况,做出相应的事情,往往需要写很多很多的If else,这样的代码可读性不强, 为了增强代码的可读性,我们可以用表驱动编程,把用来做If条件判断的值存进一个哈希表,然后从表里取值
举例:
在上面的例子中,加减乘除四个按钮我需要分别判断是哪一个按钮被点击,再修改output的值,
按照传统做法, 我们会对四个按钮分别绑定click事件,然后再分别写四个回调函数,修改值
$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 = n * 2
localStorage.setItem('n', n)
$number.text(n)
})
$button4.on('click', () => {
let n = parseInt($number.text())
n = n/2
localStorage.setItem('n', n)
$number.text(n)
})
--------用事件委托后-------
const 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)
})
}
}
复制代码
但是这样太麻烦了,更新措施:
- 绑定加减乘除按钮的父元素,就只用一个事件监听器
- 用哈希表存下按钮和按钮对应的操作
const c = {
events: {
'click #add1': 'add',
'click #minus1': 'minus',
'click #mul2': 'mul',
'click #divide2': 'div',
},
add() {
m.update({n: m.data.n + 1})
},
minus() {
m.update({n: m.data.n - 1})
},
mul() {
m.update({n: m.data.n * 2})
},
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)
}
}
复制代码
4. 模块化
- 模块化就是把相对独立的代码从一大段代码里抽取成一个个短小精悍的模块
- 每个模块之间相对独立,方便以后的维护和修改
- ES6的语法里引入了Import和export就是用来实现模块化的
当我们在app1.js 里封装好了controller 模型, 然后导出controller:
export default c // 默认导出
export {c} // 另外一种导出方式 一定要加花括号
复制代码
在Main.js里我们想用controller:
import x from './app1.js'
// 等价于
import {default as x} from './app1.js'
x.init('#app1')
复制代码
关于重命名导出的更多例子:
// inside module.mjs
export { function1, function2 };
// inside main.mjs
import { function1 as newFunctionName,
function2 as anotherNewFunctionName } from '/modules/module.mjs';
复制代码
参考资料:MDNmodules模块化
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END