MVC 是什么
架构模式的一种,MVC是三个单词的首字母缩写,它们是Model(模型)、View(视图)和Controller(控制)。
这个模式认为,程序不论简单或复杂,从结构上看,都可以分成三层(视图层,数据层,控制层)。这三层紧密联系在一起的,但又是互相独立的,每一层内部的变化不影响其他层。每一层都对外提供接口(Interface),供上面一层调用。这样一来,软件就可以实现模块化,修改外观或者变更数据都不用修改其他层,大大方便了维护和升级。
模块化
在项目中实现「模块化」,就是将一个 js 文件中的代码按功能分类为多个 js 文件,将功能相同的代码放到一个文件中。
随着应用的功能不断增加,业务逻辑越来越复杂,代码也会变得更加复杂。如果仍将所有功能代码放在一个 js 文件中,不同功能的代码散乱一团难以查找辨别,可能起变量名都会变得非常费劲,最终导致代码的可读性、复用性极差,后期难以维护。所以,为了保证「代码能有清晰的结构」、「方便查找某个功能对应的代码区」,我们依据功能不同,将代码拆分成不同的模块(文件),使各个模块之间实现「解耦」。
- 解耦:每个模块的代码都独立存在,不需要依赖其他模块。(甚至每个模块使用不同的框架。只不过体积会大一点)
- 就像我们玩的积木一样,各个积木可以组合在一起形成一个形状,又可以拆分,又可以替换,因为各个积木块都是独立的,只要他们之间的接口(形状)匹配,就可以灵活地组合在一起,解耦就是为了逐渐达到这种理想的状态。
划分模块的一个准则是「高内聚、低耦合」
- 高内聚,是指一个软件模块是由相关性很强的代码组成,只负责一项任务,也就是常说的单一责任原则。
- 低耦合,是指模块之间的联系越少越好,接口越简单越好,实现低耦合,细线通信。
- 如果各个模块之间接口很复杂,说明功能划分有不合理之处、模块之间的耦合太高,同时也说明单个模块的内聚不高。
MVC 伪代码
```javascript // 数据层,关于数据的操作放在这里 const m = { data: { // 数据初始化 n: parseInt(localStorage.getItem(‘number’) || 100)
}, update: function (data) { /更新数据/ }, delete: function (data) { /删除数据/ }, get: function (data) { /获得数据/ } }
// 视图层,关于视图的操作放在这里 const v = { el: ‘容器’, html: ‘需要插入元素内的HTML内容’, render(data){ /(获取数据)渲染html视图/ } }
// 控制层,关于事件监听的放到这里 const c = { // 找到重要的元素绑定事件 // 如果触发事件调用更改数据方法及渲染方法 a: $(‘找到a’), b: $(‘找到b’), c: $(‘找到c’), bindEvents: function(){ // bindEvents 在 render 时执行 a.on(‘click’, function(){ // 调用数据层方法更改数据 // 调用视图层方法渲染页面 }) b.on(‘click’, function(){//}) b.on(‘click’, function(){//}) } }
<a name="dKT0M"></a># 表驱动编程表驱动编程(Table-Driven Methods)是一种编程模式。<br />作用是**消除代码中频繁的 if else 或 switch case 的逻辑结构代码**,使代码更加简化- 事实上,任何信息都可以通过表来挑选。在简单情况下用逻辑语句是更简单的,但是一旦判断条件增多,那可能要写大量重复的判断语句,这时候我们通过**遍历**表来实现条件判断,将事半功倍。常规写法:看起来逻辑直白,但但随着功能复杂度的增加,代码量会线性增加```javascriptadd1(){ ... }min1(){ ... }mul2(){ ... }div2(){ ... }document.querySelector('#add1').addEventListener('click', add1)document.querySelector('#min1').addEventListener('click', min1)document.querySelector('#mul2').addEventListener('click', mul2)document.querySelector('#div2').addEventListener('click', div2)
表驱动写法:让代码具有一个稳定的复杂度,不论功能复杂度高低,代码量基本稳定。
const controller = {add1(){ },min1(){ },mul2(){ },div2(){ },events: { // 表驱动编程(对象)"click #add1": "add1", // key 的前半为要监听的事件,后半为监听的元素,value 为要执行的方法"click #min1": "min1","click #mul2": "mul2","click #div2": "div2"},autoBindEvents() {for(let key in this.events){ // 遍历对象获得对应的 key 去做赋值操作const handler = this[this.events[key]]const [event, selector] = key.split(" ") // ["click", "#min1"]$("容器").on(event, selector, handler) // 将提取出来的值去监听事件})}}
eventBus(事件总线)
作用是实现各个模块之间的通信(一对一,一对多,多对多)。
伪代码
const eventBus = $(window) //使用jquery实现updata(){//更新数据eventBus.trigger('updata') //触发updata事件}eventBus.on('updata',()=>{//监听到数据更新,执行渲染函数})
当多个应用功能都须用到 eventBus 时,将 eventBus 单独写成一个类 EventBus.js,让类继承 EventBus,这样创建的每个实例都拥有了 eventBus 上的方法。
//EventBus.jsclass EventBus{constructor(){this._eventBus =$(window)}on(eventName,fn){return this._eventBus.on(eventName,fn)}trigger(eventName,data){return this._eventBus.trigger(eventName,data)}off(eventName,fn){return this._eventBus.off(eventName,fn)}}export default EventBus //导出
//view.jsimport EventBus from './EventBus.js'class View extends EventBus{constructor(options) {super()Object.assign(this, options);this.on("m:updatad", () => {this.render(this.data); //可直接调用 eventBus 上的方法});}}export default View;
// app.jsimport Model from "./base/Model.js"const m = new Model()m.trigger('updata') //调用原型上继承的方法
