美文网首页
前端 MVC、MVVM 与面向对象

前端 MVC、MVVM 与面向对象

作者: 千反田爱瑠爱好者 | 来源:发表于2018-08-19 16:47 被阅读52次

按照《前端MVC与模块化》中的方法把各模块代码分成MVC三层,仍然存在一些问题,如公共方法无法复用、每个模块MVC中都有大量重复代码:

  • 此时可以考虑把Model、View、Controller抽象成公共模块(类),每编写一个模块通过这个模板实现;
  • 在ES5中(没有class关键字)可通过调用全局函数返回对象的方式实例化Model、View和Controller。

model.js

window.Model = Model(options){
    this.data = options.data
    this.resource = options.resource
}
window.Model.prototype.fetch = function(id){
    return axios.get(`/${this.resource}s/${id}`).then((response) => {
        this.data = response.data
        return response
      })
}
window.Model.prototype.update = function(data){
    let id = this.data.id
    return axios.put(`/${this.resource}s/${id}`, data).then((response) => {
        this.data = response.data 
        return response
    })
}

view.js

window.View = function ({el, template}){
    this.el = el
    this.template = template
}
window.View.prototype.render = function(data){
    let html = this.template
    for(let key in data){
      html = html.replace(`__${key}__`, data[key])
    }
    $(this.el).html(html)
}

controller.js

window.Controller = function (options) {
    let init = options.init
    let object = {
        init: function (view, model) {
            this.view.render(this.model.data)
            init.call(view, model)
            options.bindEvents.call()
        }
    }
    for (let key in options) {    // 写入实现对象特有的方法
        object[key] = options[key]
    }
    return object
}

实现一个功能模块:

!function () {
    let model = new Model({
        data: {
            name: '',
            number: 0,
            id: ''
        },
        resource: 'Message'
    })
    let view = View({
        el: '.messageBoard',
        template: `
                <div>
                    书名:《__name__》
                    数量:<span id=number>__number__</span>
                </div>
                <div>
                    <button id="addOne">加1</button>
                    <button id="minusOne">减1</button>
                    <button id="reset">归零</button>
                </div>
        `
    })
    let controller = Controller({
        init: function(view, model) {
            this.model.fetch(1).then(() => {
                this.view.render(this.model.data)
            })
        },
        bindEvents: function() {
            $(this.view.el).on('click', '#reset', this.reset.bind(this))
            $(this.view.el).on('click', '#addOne', this.addOne.bind(this))
            // ...
        },
        addOne: function() {
            var oldNumber = $('#number').text()
            var newNumber = oldNumber - 0 + 1
            this.model.update({
                number: newNumber
            }).then(() => {
                this.view.render(this.model.data)
            })
        },
        reset() {
            this.model.update({
                number: 0
            }).then(() => {
                this.view.render(this.model.data)
            })
        },
    })
    controller.init({view: view, model: model})
}.call()

MVVM

使用Vue自动实现MVC:


MVVM

其中ViewModel实现了MVC中Controller的功能:

  • 当View层DOM发生变化时可通过监听器通知Model层修改数据;
  • 当Model层数据发生变化,可通过与View层DOM的绑定修改DOM元素。

html

<div id="app">
</div>

script

    initData()

    // MVC类(C封装在V内)
    function Model(options) {
        this.data = options.data
        this.resource = options.resource
    }
    Model.prototype.fetch = function (id) {
        return axios.get(`/${this.resource}s/${id}`).then((response) => {
            this.data = response.data
            return response
        })
    }
    Model.prototype.update = function (data) {
        let id = this.data.id
        return axios.put(`/${this.resource}s/${id}`, data).then((response) => {
            this.data = response.data
            return response
        })
    }

    // 对象
    let model = new Model({
        data: {
            name: '', number: 0, id: ''
        },
        resource: 'book'
    })

    let view = new Vue({
        el: '#app',
        data: {
            book: {
                name: '未命名',
                number: 0,
                id: ''
            },
            n: 1
        },
        template: `
        <div>
            <div>
                书名:《{{book.name}}》
                数量:<span id=number>{{book.number}}</span>
            </div>
            <div>
                <input v-model="n" /> N 的值是 {{n}}
            </div>
            <div>
                <button v-on:click="addOne">加N</button>
                <button v-on:click="minusOne">减N</button>
                <button v-on:click="reset">归零</button>
            </div>
        </div>
        `,
        created() {
            model.fetch(1).then(() => {
                this.book = model.data
            })
        },
        methods: {
            addOne() {
                model.update({
                    number: this.book.number + (this.n - 0)
                }).then(() => {
                    this.view.book = this.model.data
                })
            },
            minusOne() {
                model.update({
                    number: this.book.number - (this.n - 0)
                }).then(() => {
                    this.view.book = this.model.data
                })
            },
            reset() {
                model.update({
                    number: 0
                }).then(() => {
                    this.view.book = this.model.data
                })
            },
        }
    })

    function initData() {
        let book = {
            name: 'JavaScript 高级程序设计', number: 2, id: 1
        }
        axios.interceptors.response.use(function (response) {
            let {
                config: {method, url, data}
            } = response
            if (url === '/books/1' && method === 'get') {
                response.data = book
            } 
            else if (url === '/books/1' && method === 'put') {
                data = JSON.parse(data)
                Object.assign(book, data)
                response.data = book
            }
            return response
        })
    }

this 关键字

  • this是call函数的第一个参数;
  • 函数调用controller.init(view, model)的本质是controller.init.call(controller, view, model),在init内部this指的就是controller;
  • 因此无法理解函数内部this的指向时,应改用func.call(this)的方式调用(如定义一个没有参数的函数,实际调用时却传入了参数,则在该函数内部的this即为调用时传入的第一个参数)。
function X() {
    return object = {
        name: 'object',
        options: null,
        f1(x) {
            this.options = x    // 此处x实际上是外部传入的第二个参数,即options
            this.f2()
        },
        f2() {
            this.options.f2.call(this)    // 此处this是x,即'object'
        }
    }
}

var options = {
    name: 'options',
    f1(){},
    f2(){
        console.log(this)    // 此处this是由外部传入,即'object'
    }
}

var x = X()
x.f1(options)    // x.f1.call(x, options)

call、apply与bind

  • apply、call和bind都是来改变this的指向的,其第一个参数都是相同的this指向的对象;
  • apply接收的第二个参数为数组,call和bind接收的是多个参数;
  • apply与call会立即被执行,bind只是先改变this的指向(绑定)。
var s = {
    a:1,
    b:2,
    add(name) {
        console.log(this.a + this.b)
        console.log(name)
    }
}
var a = {
    a: 2, b: 2,
}
var b = {
    a: 4, b: 2,
}
var c = {
    a: 5, b: 6,
}
s.add.call(a,"call")
s.add.apply(b,["apply"])  
s.add.bind(c,"bind")()    // 等价于s.add.bind(c)("bind")

new 关键字

  • 我们往往会不经意地创建很多对象,这些对象中很多方法都是一样的,只需要各自引用同一个函数即可,没有必要重复创建;
  • 对于对象的公有属性(如某兵种的攻击力)可以抽象出来,而对象私有部分(如姓名和id)则可以单独创建;
  • 使用new关键字可以不用创建和返回临时对象(使用this就可以访问到),而不需要绑定原型(名字为prototype);同时定义constructor属性可以记录“临时对象是由哪个函数创建的”:
function Human(options) {    // Human类的构造函数,传入对象必须包括name、city参数
    this.name = options.name    
    this.city = options.city
}

Human.prototype = {
    constructor: Human,    // 记录通过new Human()创建的对象类型即为Human
    species: function(){},
    walk: function(){},
    useTools: function(){}
}

var human = new Human({name:'ywh', city: 'gz'})
human.__proto__                          // 对应的对象(即原型)具有species、walk和useTools这几个属性
human.__proto__.constructor === Human    // 表示human的类型为Human

相关文章

  • 前端 MVC、MVVM 与面向对象

    按照《前端MVC与模块化》中的方法把各模块代码分成MVC三层,仍然存在一些问题,如公共方法无法复用、每个模块MVC...

  • Vue

    Node.js(后端)中的MVC与前端中的MVVM之间的区别 MVC是后端的分层开发概念,MVVM是前端视图层的概...

  • JS III MVC、MVVM、面向对象

    一、面向对象 面对对象就是: 把数据及对数据的操作方法放在一起,作为一个相互依存的整体——对象。对同类对象抽象出其...

  • iOS设计(二)项目架构2 MVVM

    一、架构方案选择 1. 面向协议编程 2. 面向接口编程(MVP) 3. MVC&MVVM 3.2、MVVM...

  • iOS设计(二)项目架构1 MVC

    一、架构方案选择 1. 面向协议编程 2. 面向接口编程(MVP) 3. MVC&MVVM 3.1、MVC模...

  • 关于架构思想的看法

    目前开发中用到的主流思想主要是MVC 、 MVP、 MVVM这三种思想。前端用的MVVM,后端用的MVC,安卓用...

  • Promise学习资料 - 收藏集 - 掘金

    Flux 架构模式 - 前端 - 掘金 在说flux模式之前,我们先说说mvc和mvvm模式 MVC模式 ... ...

  • 关于 Vue、React 与 MVVM

    首先要明确 MVVM 是什么,它是 MVC 的衍生架构。无论是 MVC 还是 MVVM 都不是只针对于前端或后端开...

  • iOS面试题:MVVM和MVC的区别

    MVVM和MVC的区别 1. MVC MVC的弊端 厚重的View ControllerM:模型model的对象...

  • MVVM in Swift

    参考文章: MVVM in Swift MVC 与MVVM的区别在MVC下ViewController中常常会包含...

网友评论

      本文标题:前端 MVC、MVVM 与面向对象

      本文链接:https://www.haomeiwen.com/subject/knwciftx.html