MVC浅析

267 阅读3分钟

MVC

  • M - Model : 数据模型 负责操作数据
const m = {
    data: {
        n: parseInt(localStorage.getItem("n"))
    }
}
  • V - View : 视图 负责UI界面
const v = {
   el: null,
    html: `
        <div>
            <div id="content">{{n}}</div>
            <div id="buttons">
                <button id="add">加2</button>
                <button id="minus">减2</button>
                <button id="multiply">乘2</button>
                <button id="division">除2</button>
            </div>
         </div>
    `,
    init(el) {
        v.el = $(el)
    },
    render(n) {
        if (v.el.children.length !== 0) v.el.empty()
        $(v.html.replace('{{n}}', n)).appendTo(v.el)
    }
  }
  • C - Controller 控制器 负责其他
const c = {
    events: {
        ' click #add': 'add',
        ' click #minus': 'minus',
        ' click #multiply': 'multiply',
        ' click #division': 'division'
    },
    add() {
        console.log('here');
        m.update({n:m.data.n+2})
    },
    minus() {
        m.update({n:m.data.n-2})
    },
    multiply() {
        m.update({n:m.data.n*2})
    },
    division() {
        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)

        }
    }
}

模块化

  • 模块就是实现特定功能的一组方法。
  • 各模块各司其职,互不影响,标准化的接口
  • 当有该模块功能的需求时直接调用
  • 每个文件的代码都会小而清晰
  • 代码复用

最小知识原则

  • 引入一个模块需要引入 js
  • 你需要知道的知识越少越好

不把 app1.js 写死,实现随便传一个可用参数都能实现他的功能。

// 在 app1.js 里把 c 暴露出去 
export default c
// 在 main.js 导入 x ,x 即为 app1.js 里的c 
import x from "./app1.js";
// 实现在 app1.js 外部使用其功能
x.init("#app1")

问题

页面一开始可能是空白的,可加菊花、骨架、占位内容等优化

以不变应万变

  • 每个模块都使用 m + v + c 架构
  • 不用考虑类似的需求如何做,自己套用即可

表驱动编程

  • 当出现大量类似但不重复的代码
  • 提取重要数据,将其做成哈希表

对一个个按钮绑定事件,除了按钮与对应的触发事件不同,其他都是一样的。使用表驱动编程优化代码。

events:{
    '#btnPause': 'pause',
    '#btnSlow': 'slow',
    '#btnNormal': 'normal',
    '#btnFast': 'fast'
  },
  bindEvents:()=>{
    for(let key in player.events){
    if(player.events.hasOwnProperty(key)){
      const value = player.events[key]
      document.querySelector(key).onclick = player[value]
    }
    }
  },

eventBus

当 C 中的按钮监听事件触发时,都要将 data 修改,重新渲染页面,即调用 V 中的 render(m.data.n),因此每个按钮监听事件都要显示的加上 v.render(m.data.n),如何优化?

引用eventBus

  1. 在 M 中写一个 update()方法,将 eventBus 的事件触发 xxx 添加进去
update(data) {
        Object.assign(m.data, data)
        //事件触发
        eventBus.trigger('xxx')
    }
  1. 在 C 中的按钮事件监听中执行 update() 方法,相当于触发事件 xxx
  2. 在 V 中写 eventBus 的事件监听 xxx 方法,监听到 xxx 事件的触发就执行render
eventBus.on('xxx',()=>{
            v.render(m.data.n)
        })

因此,eventBus实现了对象间的通信,也可看出 M 层与 V 层的关系紧密,实现数据与页面同步进行。

view = render(data)

  • 操作 DOM 对象渲染页面的机制是 js 从页面获取数据,DOM 处理数据后渲染页面
  • 事实上,比起操作 DOM ,直接 render 简单多了
  • js 通过自身获取了数据,直接 render 渲染页面,数据修改的同时 render 渲染页面。

弊端

  • render 粗犷的渲染比起 DOM 操作浪费性能
  • 虚拟 DOM 可以解决
  • 它能让 render 只更新该更新的地方

对象原型与公用属性 ----建立中间层,隔绝细节

  • 既然所有的模块都是 MVC 架构 ,那么对 MVC 进行改写
  • 创建一个类,将 M 里边的属性与方法分别写入类的自身属性与方法中
  • M 是类的实例并传入对应的参数
  • 不同模块中的 M都可成为该类的实例,使用类的方法
  • 其它类似

继承类

  • 既然所有的模块都需要 EventBus 来实现对象间的通信
  • 创建一个 EventBus 类,写好触发事件、监听事件等方法
  • 让创建的类都去继承 EventBus 类
  • 实例都可拥有 EventBus 的方法,就无需重写多次