0
点赞
收藏
分享

微信扫一扫

MVC(下)

萨科潘 2022-03-12 阅读 75

MVC(下)

主要内容:使用类优化代码
1.引入类(class)
2.引入继承(class继承class)
3.初识Vue

抽象思维四:事不过三

同样的代码写三遍,就应该抽成一个函数
同样的属性写三遍,就应该做成共用属性(原型或类)
同样的原型写三遍,就应该用继承
代价
有的时候会造成继承层级太深,无法一下看懂代码
可以通过写文档、画类图解决

步骤
对比MVC的app1.js 和 app2.js,属性几乎一模一样。
何不把公共的属性抽成公共属性/原型,在js里公共属性就是原型。
它们两个v的属性一模一样,可以抽成原型。
它们两个c的公共属性抽到公共属性里。
在这里插入图片描述

第一部分:先抽m

步骤
(1)新建目录base存放基础代码,新建文件Model.js(js中的类都大写)。

class Model {
}

(2)抽离共用的

//(2)所有的model都有增删改查4个属性,不需要实现。
class Model {
  constructor(options) { //data初始化时给我
    this.data = options.data
  }
  create() {
    console?.error?.("你还没实现create")//兼容IE
  }
  delete() {
    console?.error?.("你还没实现delete")
  }
  update() {
    console?.error?.("你还没实现update")
  }
  get() {
    console?.error?.("你还没实现get")
  }
}
export default Model

(3)使用

app1.js
import Model from './base/Model.js' //.js可加可不加

const m = new Model({ //使用
  data: {
    n: parseInt(localStorage.getItem('n'))
  }
})
m.update = (data) => {
  Object.assign(m.data, data)
  eventBus.trigger("m:updated")
  localStorage.setItem('n', m.data.n)
}
//console.dir(m)  构造函数或类
//m.create() 还没实现所有报错

函数都在原型里,只有data在当前对象本身上。注:无需逗号
在这里插入图片描述

最终版本

Model.js
constructor(options) {
/*this.data = options.data
  this.create = options.create
  this.delete = options.delete
  this.update = options.update
  this.get = options.get
*///简化为
  ['data','create','delete','update','get'].forEach((key) =>{ //1'遍历的写法
    if (key in options) {
      this[key] = options[key]
    }
  })  
}
  create() {
    console?.error?.("你还没实现create")
  }
  delete() {
    console?.error?.("你还没实现delete")
  }
  update() {
    console?.error?.("你还没实现update")
  }
  get() {
    console?.error?.("你还没实现get")
  }
}
export default Model

app1.js
import Model from './base/Model.js'
const m = new Model({
    data: {
        n: parseInt(localStorage.getItem('n'))
    },
    update(data) { //不推荐用箭头函数
        Object.assign(m.data, data)
        eventBus.trigger("m:updated")
        localStorage.setItem('n', m.data.n)
    }
})
//console.dir(m)

app2.js
import Model from './base/Model.js'
const m = new Model({
  data: {
    index: parseInt(localStorage.getItem(localKey) || 0)
  },
  update(data) {
    Object.assign(m.data, data)
    eventBus.trigger("m:updated")
    localStorage.setItem('index', m.data.index)
  }
})

在面向对象里箭头函数非常容易出错,所以不推荐用。
函数都在当前对象本身上。
在这里插入图片描述

接收参数的2种处理方法:
1’ 当参数多时用遍历的写法
2’不遍历的写法:结构化

第二部分:简化v、c

步骤
(1)新建View.js (js中的类都大写)。

class View {
}

(2)抽离共用的

(3)使用

//(2)抽离共用的
import $ from 'jquery'
class View {//初始化写到constructor上
  constructor({ el, html, render }) {//2'不遍历的写法:结构化
    this.el = $(el)
    this.html = html
    this.render = render
  }
}
export default View

//(3)合并v、c,抽离前
app1.js
import View from './base/View.js'
const view = {
  el: null,
  html: `...`,
  init(container) {
    view.el = $(container) //接收的el会被作为容器
    view.render(m.data.n)
    view.autoBindEvents()
    eventBus.on('m:updated', () => {
      view.render(m.data.n)
    })
  },
  render(n) {
    if (view.el.children.length !== 0) view.el.empty()
    $(view.html.replace('{{n}}', n)).prependTo(view.el)
  },
  ...
export default view
app2.js
import Model from './base/Model.js'
import View from './base/View.js'
//方法:把v拷到c,2个init合并成1个
...
export default init //导出初始化方法
//(3)抽离后

app1.js
app2.js

优化eventBus
既然所有的View、M都要用eventBus,那我们可以把eventBus收拢放一个地方。
步骤
新建文件EventBus.js
之前我们用的是jquery的eventBus,实际上应该自己写。

import $ from "jquery"

class EventBus {
  constructor() {
    this._eventBus = $(window)
  }
  trigger(eventName, data) {
    return this._eventBus.trigger(eventName, data)
  }
  on(eventName, fn) {
    return this._eventBus.on(eventName, fn)
  }
  off(eventName, fn) {
    return this._eventBus.off(eventName, fn)
  }
}
export default EventBus

使用

app1.js
import EventBus from './base/EventBus'

const eventBus = new EventBus()
eventBus.trigger()
app2.js  ...

代码明显变多了,这样写到底有什么好处?
优点:这样写eventBus可以随时改变eventBus的实现。现在是用jquery实现的,如果哪天突然不用jquery了,直接在这个模块(EventBus.js)里把jquery删掉即可。

继承EventBus

步骤
1.先继承
如果你继承了一个类就必须在初始化constructor里调用类的父类的初始化。

Model.js
import EventBus from "./EventBus"
class Model extends EventBus {
  constructor(options) {
    super() //调用EventBus#constructor()
  }
}
View.js ...

2.后使用

app1.js
 m.trigger("m:updated")
 //继承后就不需要再给eventBus赋值了
View.js
this.on() 
app2.js ...

js代码不能以[]开头(重要)
JS要么每句话加";" ,要么不能以[]开头,二选一。
在这里插入图片描述

初识Vue

由于目前我们的MVC不够完善,所以选择用vue来演示。
parcel快速搭建Vue项目
1.导入Vue

先安装: yarn add vue@2.6.10 //指定版本
重启: parcel src/index.html
导入:import Vue from 'vue' //app1.js

//console.log(Vue) //函数,导入成功

2.新建Vue

const init = (el) => {
    new Vue({
        el: el
    })
}

去除警告:
在这里插入图片描述

新建Vue时报错,提示要切换为Vue完整版,默认是不完整版。
方法: 在package.json添加

"alias": {
  "vue$" : "./node_modules/vue/dist/vue.common.js"
}

最后重启parcel
在这里插入图片描述

1.Vue认为m也没必要写,那怎么替换呢? {{n}} //占位
2.Vue如何绑定事件?@click=""

<button @click="add"> +1 </button>
<button @click="minus"> -1 </button>
<button @click="mul"> *2 </button>
<button @click="divide"> /2 </button>
methods:{  //事件
  add() { this.n +=1 },
  minus() { this.n -= 1 },
  mul() { this.n *= 2 },
  divide() { this.n /= 2 }
}

3.Vue如何保存值?
监听事件变化

watch: {
  n() { //当n变化时执行函数 n:function(){}
    localStorage.setItem('n', this.n)
  }
}

render不用写。只用绑定事件就好。
4.Vue认为m、c都不重要,Vue基本上就到了MVC简化的极限了,这也是Vue这么流行的原因。

const init = (el) => {
  new Vue({
    el: el,
    data: {
      n: parseFloat(localStorage.getItem('n'))//Vue认为m也没必要写
    },
    methods: {
      add() {
        this.n += 1
      },
      ...
  },
    watch: {
      n() { //当n变化时执行函数 n:function(){}
        localStorage.setItem('n', this.n)
      }
    },
    template: `
      <section> //<section>会被替换
        ...
      </section> `
  })
}

5.:class="" //加前缀:表示class里的是js代码
render不用写。只用绑定事件就好。

6.表驱动编程是做什么的?

const list={
    "小明":10,
    "小白":14,
}
function age2(name){
    if(name in list){
        console.log(name+"的年龄是"+list[name])
    }else{
        console.log("查无此人")
    }
}

7.如何理解模块化?
模块化(modular)编程,是强调将计算机程序的功能分离成独立的、可相互改变的“模块”(module)的软件设计技术,它使得每个模块都包含着执行预期功能的一个唯一方面(aspect)所必需的所有东西。

在一个完整的页面应用中,不同的节点功能,不同的结构可以规划为多个模块,每个模块的实现的方式以及用到的技术大不相同,使用模块化编程可以减小各个模块之间的影响和联系,可以更方便的优化代码和重构代码,提高我们代码的重用性,便于后期维护。

总结

Vue思想在MVC思想中的体现
抽象思维1:最小知识原则

抽象思维2:MVC思想
虽然MVC思想在Vue里浓缩的只有v了,但思想一直在。只不过当你需求太简单时不会再想着把它分开,但当你复杂的时候你还是得分开。Vue只能满足最基础的需求,当你的应用复杂时MVC就还是MVC,Vue就还是MVC。

const m = {
  get(){ return parseFloat(localStorage.getItem('n')),
  set(n){ localStorage.getItem('n',this.n),
}
new Vue({
  data:{ n:m.get() },
  watch:{
    n:function(){
      m.set(this.n)
    }
  }
})

抽象思维3:表驱动编程
Vue里methods、watch就是表,你只要告诉我什么东西变化了做什么,我就能帮你做。因为我把表之外的逻辑都写好了。

抽象思维4:事不过三原则

const v=new Vue({ ... })
console.log(v) //打印Vue的实例

第1、2层是Vue给它的一些属性,第3层$on 、$emit 、$off 、$onceemit就是trigger,Vue所有内置的方法都用$开头。

Vue它也是个eventBus,所以我们可以直接用Vue做eventBus:

const init = (el) => {

    const eventBus = new Vue()
    //eventBus.$emit() //trigger
    //eventBus.$on()
    //eventBus.$off()
    console.log(eventBus.$emit)
    console.log(eventBus.$on)
    console.log(eventBus.$off)
}    

抽象思维5:俯瞰全局(实现eventBus)
把所有的对象看成点
一个点和一个点怎么通信
一个点和多个点怎么通信
多个点和多个点怎么通信
最终我们找出一个专用的点负责通信,这就是eventBus(事件总线)

抽象思维6:view=render(data)
vue没有体现,但react体现了,react会显示的让你调用render。Vue之所以没有体现是觉得太复杂了它自己给简化了。
Vue只要发现n变化了就自动帮你局部渲染。Vue的更新机制比这个更加精致,只变该变的。
测试:
在+1按钮上添加id="xxx"点击+1后检查id是否存在。如果不见了说明被重新渲染了。
结果:只会局部渲染<span>,id还在。
说明button从来没有被移除过页面。

重点
1.MVC的概念
2.eventBus的概念
3.view=render(data)概念
4.表驱动编程概念
5.类什么时候用
6.继承什么时候用

举报

相关推荐

0 条评论