关于前端:浅析MVC

53次阅读

共计 5017 个字符,预计需要花费 13 分钟才能阅读完成。

一、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)
// 数据相干都放到 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

二、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.mjs
export {function1, function2};

// inside main.mjs
import { function1 as newFunctionName,
         function2 as anotherNewFunctionName } from '/modules/module.mjs';

正文完
 0