「一」MVC三个对象
- 把页面分成很多模块,每个模块可以写成3个对象,分别是M、V、C
- M —— Model,数据模型,负责数据相关的任务,操作所有数据(增删改查)
- V —— View,视图,负责所有UI界面,即用户能看得到的界面
- C —— Controller,控制器,负责监听用户事件,然后调用 M 和 V 更新数据和视图
1. Model(数据模型)
Model 数据管理,包括数据逻辑、数据请求、数据存储等功能。
前端 Model 主要负责 AJAX 请求或者 LocalStorage 存储。
伪代码示例
const model = {
data: { 初始化数据 },
create() { 增数据 },
delete() { 删数据 },
update(data) { 更新数据 },
get() { 读数据 }
}
2. View(视图)
View 负责用户界面,前端 View 主要负责 HTML 渲染。
伪代码示例
const view={
// 需要刷新的元素
el: null,
// 显示在页面上的内容
html: `<div class="html"></div>`,
// 初始化html
init(container){
v.el = $(container)
},
// 刷新页面
render() { 将数据渲染到页面 }
}
3. Controller(控制器)
Controller 负责处理 View 的事件,并更新 Model;也负责监听 Model 的变化,并更新 View,Controller 控制其他的所有流程。
伪代码示例
const controller = {
init(){
v.init() // view初始化
v.render() // 第一次渲染 view = render(data)
c.autoBindEvents() // 自动绑定事件
eventBus.on('m:update', () => { v.render() }) // 当eventBus触发'm:update'时View刷新
},
events:{ 事件以哈希表方式记录 },
method() {
data = 改变后的新数据
m.update(data)
},
autoBindEvents() { 自动绑定事件 }
}
4. MVC实例
一个加减乘除计算器
每次点击对应的按钮,数值就会变,基本思想就是监听click事件。
其MVC操作如下:
代码展示
import './app1.css'
import $ from 'jquery'
const eventBus = $(window)
//数据相关 都放到 M
const m = {
data: {
// 初始化数据n,从本地数据库拿到的数字n
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>
<button id="add1">+1</button>
<button id="minus1">-1</button>
<button id="mul2">×2</button>
<button id="divide2">÷2</button>
</div>
</div>
`,
// 初始化html
init(container){
v.el = $(container)
},
// 将数据渲染到页面
render(n){
if(v.el.children.length === null){ // 如果没有渲染过
} else {
v.el.empty()
}
$(v.html.replace('{{n}}', n)).appendTo(v.el)
},
}
//其他 都 C
const c = {
init(container){
v.init(container) // 第一次渲染html
v.render(m.data.n) // 第一次 view = render(data)
c.autoBindEvents() // 绑定事件
eventBus.on('m:updated', ()=>{
v.render(m.data.n)
})
},
events: {
'click #add1': 'add',
'click #minus1': 'minus',
'click #mul2': 'mul',
'click #divide2': 'div'
},
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)
}
},
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})
}
}
export default c
「二」 EventBus
- EventBus 就是 EventTarget
- EventBus也是一种设计模式或框架,主要用于组件/对象间通信的优化简化。
- 比如在上面的例子中,Model 数据模型 和View 视图模型彼此不知道彼此的存在,但是又需要通信,于是就要用到EventBus
- 优势 使用 eventBus 可以满足最小知识原则,m 和 v 互相不知道对方的细节,但是却可以互相调用对方的功能;代码简洁;速度快且轻量。
EventBus API
- eventBus 提供了
on
、off
和trigger
等 API,on
用于监听事件,trigger
用于触发事件,off
用于取消监听
代码实例
- 在JS文件中自己声明一个EventBus,来模拟使用EventBus的功能。为了方便起见,这里通过引入jquery实现。
import $ from 'jquery'
const eventBus = $(window)
- 在一个地方使用on方法,另外一个地方使用trigger方法,就初步实现了数据通信功能。
- 在上面的MVC模型中, Model数据模型更新时,会 trigger 触发一个事件
- 然后在controller会用 on 监听事件, 通知 view 模型去重新渲染页面
const m = {
....
update(data) {
Object.assign(m.data, data)
eventBus.trigger('m:updated') // 通知一下view层,我已经更新了数据,view该开始工作了
localStorage.setItem('n', m.data.n)
},
....
}
const c = {
init(container) {
v.init(container)
v.render(m.data.n) // view = render(data)
c.autoBindEvents()
eventBus.on('m:updated', () => { // 用 on 监听事件, 通知 view 模型去重新渲染页面
console.log('here')
v.render(m.data.n)
})
},
...
}
「三」表驱动编程
- 表驱动法(Table-Driven Approach),简单讲是指用查表的方法获取值。
- 表驱动法是一种编程模式,表里可以存数据,也可以存指令,或函数等都可以。
- 在数值不多的时候我们可以用逻辑语句(if/else 或 case do)的方法来获取值,但随着数值的增多逻辑语句就会越来越长,此时我们可以用表驱动编程,把用来做If条件判断的值存进一个哈希表,然后从表里取值。
- 表驱动编程在 JavaScript 中的一个重要应用是自动绑定事件。
代码展示
在上面的MVC实例中,加减乘除四个按钮我需要分别判断是哪一个按钮被点击,再修改output的值,
用事件委推,绑定事件中有jquery的事件监听方法。
--------用事件委托后-------
const c = {
init(container) {
v.init(container)
v.render(m.data.n)
c.BindEvents() //绑定事件
}
BindEvents() {
//jquery对象
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.用哈希表存下按钮和按钮对应的操作
const c = {
init() {
...
// 称为自动绑定事件
c.autoBindEvents()
},
// 事件 用 哈希表
events: {
'click #add1': 'add',
'click #minus1': 'minus',
'click #mul2': 'mul',
'click #divide2': 'div'
},
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)
}
},
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})
}
}
「四」模块化
-
一个web页面,根据不同的功能可以分出不同的模块,每个模块的实现方式和使用的技术等等都不相同,引入模块化,可以切断每个模块的相互影响,使得单个模块中可以更好的优化代码。
-
一个模块就是一个独立的文件,该文件内部的所有变量,外部无法获取,如果外部想读取模块内部的某个变量,ES6的语法里引入了
import
和export
用来实现。 -
模块化可以降低代码耦合度,减少重复代码,提高代码重用性,并且在项目结构上更加清晰,便于维护。
References
- developer.mozilla.org/zh-CN/docs/…
- zhuanlan.zhihu.com/p/96985491
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!