一、MVC是什么?
M 就是 model, 即数据模型,负责数据相干的工作,包含对数据的增删改查
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 MVC 实例
咱们的指标是做一个加减乘除计算器
每次点击加、减、乘、除按钮,数值就会变,根本思维就是监听click事件
import './app1.css'import $ from 'jquery'const eventBus = $(window)// 数据相干都放到mconst 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() {}}// 视图相干都放到vconst 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) }}// 其余都cconst 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
二、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种以上的状况,做出相应的事件,往往须要写很多很多的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) }) }}
然而这样太麻烦了,更新措施:1. 绑定加减乘除按钮的父元素,就只用一个事件监听器 2.用哈希表存下按钮和按钮对应的操作
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) } }
四、模块化
模块化就是把绝对独立的代码从一大段代码里抽取成一个个短小精悍的模块
每个模块之间绝对独立,不便当前的保护和批改
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.mjsexport { function1, function2 };// inside main.mjsimport { function1 as newFunctionName, function2 as anotherNewFunctionName } from '/modules/module.mjs';