最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 从零实现简易MVC

    正文概述 掘金(用户5919911482690)   2021-03-24   469

    MVC是什么?

    MVC是一种架构设计模式,它的核心是关注点的分离,即将模型、视图、控制器分离开来。

    M模型、V视图、C控制器

    • 模型model封装了与业务逻辑相关的数据以及对数据的处理方法,即负责操作所有数据
    • 视图view是它在屏幕上的表示,描绘的是model的当前状态。即负责所有 UI 界面
    • 控制器controller定义用户界面对用户输入的响应方式,起到不同层面间的组织作用,用于控制应用程序的流程,它处理用户的行为和数据model上的改变。即负责M、V之外的其他事情

    从零实现简易MVC

    经典MVC模型图

    实现一个简易MVC

    为了更好地理解MVC模式,我们来用js实现一个简易的mvc。创建一个文件夹,目录结构如下:

            mvc-demo
              |-- index.html
              |-- main.js 
              |-- app.js
              |-- src
                  |-- model.js
                  |-- view.js
                  |-- controller.js
    

    使用 npm init -y 命令初始化项目,此命令需要已安装nodejs。

    M(modle层)

    在src/mvc/model.js中定义一个模型类,该类拥有增删改查数据的方法,并通过new实例时初始化数据。

    class Model {
        // 数据模型初始化:绑定数据
        constructor(data) {
            this._data=data
        }
        // 提供数据增删改查操作
        create () { }
        delete () { }
        update (data) {this._data=data}
        get () { return this._data }
    }
    export default Model
    

    V(view层)

    在src/mvc/view.js中定义一个视图类,提供视图更新操作,new实例时初始化页面设置(绑定页面元素和html模板)

    class View {
        // 视图初始化
        constructor(el,template) { 
            this.el = document.querySelector(el)
            this.template = template
        }
        render () { } // 渲染视图
    }
    export default View
    

    C(controller层)

    在src/mvc/controller.js中定义一个控制器类,将上述的M、V层组织起来,进行事件绑定、视图更新渲染,处理相关逻辑。

    
    class Controller{
        constructor(model, view, methods) {
            this.model = model
            this.view = view
            this.methods=methods
            this.init()
            this.bindMethods()
        }
        // 初始化方法
        init () {
            // 实现视图渲染方法
            this.view.render = () => { this.view.el.innerHTML = this.view.template.replace('{{data}}',this.model.get()) }
            this.view.render() //渲染视图
        }
        // 绑定按钮事件
        bindMethods () {
            [].forEach.call(this.methods, element => {
                // 采用事件委托的方式绑定事件到容器元素上,防止视图重新渲染时子元素被替换导致监听事件失效
                this.view.el.addEventListener('click',  (e) => {
                    if (e.target.id === element.DomID)
                    {
                        element.fn() // 执行对应方法
                        this.view.render() // 重新渲染视图
                    }
                })
            })
        }
    }
    export default Controller
    

    使用这个简易的MVC

    简单起见,我们实现一个四则运算计算器,页面上只提供数据和四个加减乘除操作按钮。 src/index.html内容如下

    <body>
        <div id="app"></div>
        <script src='./main.js'></script>
    </body>
    

    在src/main.js中导入这几个类,写好业务逻辑和相关配置,实现他们。

    import Model from "./mvc/model"
    import View from "./mvc/View"
    import Controller from "./mvc/controller"
    const options = {
        data: 100,
        el:'#app',
        template: `<div>{{data}}<div>
        <div><button id="add">+1</button></div>
        <div><button id="sub">-1</button></div>
        <div><button id="mul">x10</button></div>
        <div><button id="div">/10</button></div>
        `,
        methods: [
            {
                DomID: 'add',
                fn: () => { model.update(model.get()+1) }
            },
            {
                DomID: 'sub',
                fn: () => { model.update(model.get()-1) }
            },
            {
                DomID: 'mul',
                fn: () => { model.update(model.get()*10) }
            },
            {
                DomID: 'div',
                fn:  () => { model.update(model.get()/10) }
            }
        ]
    }
    const { data, el, template, methods }=options
    const model = new Model(data)
    const view = new View(el, template)
    const controller = new Controller(model, view, methods)
    

    试试效果

    从零实现简易MVC

    优化封装

    我们已经成功地实现了一个简易mvc框架,并成功使用它制作了一个小demo,但这个框架还有诸多可以改善的地方。比如代码封装不够简洁,如果要再写一个demo,又得重新写一大串代码,能不能让我只配置相关的数据和方法就好呢?接下来我们开始优化。

    再加一层?

    软件工程中有这么一句话:没有什么是加一层解决不了的。接下来我们加一层app.js来实现一个mvc具体实现代码的封装,依据最小知识原则,我们在使用app.js创建项目时应该只需要传最少的配置,因此我们来实现一个app实现类。 在src/app.js中实现App类,该类通过构造函数传入配置参数,自动帮我们实例化了mvc每个类的操作,使得我们调用这个类时只需要传一个options参数即可。

    import Model from "./mvc/model"
    import View from "./mvc/View"
    import Controller from "./mvc/controller"
    class App{
        constructor({ data, el, template, methods }) {
            this.model = new Model(data)
            this.view = new View(el, template)
            this.controller = new Controller(this.model, this.view, methods)
        }
    }
    export default App
    

    在src/main.js中引入app.js并配置相关数据参数。由于此时的methods已经无法直接调用model类,因此写成function函数,用this代替,并在controller添加监听事件时绑定this到model。

    import App from './app'
    new App({
        data: 100,
        el:'#app',
        template: `<div>{{data}}<div>
        <div><button id="add">+1</button></div>
        <div><button id="sub">-1</button></div>
        <div><button id="mul">x10</button></div>
        <div><button id="div">/10</button></div>
        `,
        methods: [
            {
                DomID: 'add',
                fn: function () { this.model.update(this.model.get()+1) }
            },
            {
                DomID: 'sub',
                fn: function () { this.model.update(this.model.get()-1) }
            },
            {
                DomID: 'mul',
                fn: function () { this.model.update(this.model.get()*10) }
            },
            {
                DomID: 'div',
                fn: function () { this.model.update(this.model.get()/10) }
            }
        ]
    })
    

    controller.js修改内容:

        // 绑定按钮事件
        bindMethods () {
            [].forEach.call(this.methods, element => {
                // 采用事件委托的方式绑定事件到容器元素上,防止视图重新渲染时子元素被替换导致监听事件失效
                this.view.el.addEventListener('click',  (e) => {
                    if (e.target.id === element.DomID)
                    {
                        element.fn.call(this,arguments) // 执行对应方法
                        this.view.render() // 重新渲染视图
                    }
                })
            })
        }
    

    之后如果想再写一个新的demo,就可以直接复用app.js,简单的传入一些参数:容器元素、数据、绑定事件,就搞定啦,是不是很简洁?

    有点像Vue?

    app.js封装之后,引入app并传配置的步骤怎么越看越像Vue呢?其实,Vue的MVVM思想就是在MVC的基础之上发展而来的。我们先来看一下MVVM的特点。

    M(Model模型) MVVM中的M保存的是每个页面中单独的数据

    VM(ViewModel) 它是一个调度者,分割了M和V每当V想要获取后面保存数据的时候,都要由VM做中间的处理

    V(view视图) 就是每个页面中的HTML结构

    VM是MVVM的思想核心,是 M 和 V 之间的调度者,数据的双向绑定是由 VM 提供的; MVVM与MVC最大的区别就是:MVVM实现了View和Model的自动同步,不用再自己手动操作Dom元素了,即Model变化时View可以实时更新,View变化也能让Model改变。 因此理解了MVC,我们就理解了Vue的设计思路,因为他们本质上都是一样的,都是通过分离关注点的设计,将代码抽象隔离开来。

    MVC、MVVM傻傻分不清楚?

    MVVM的出现是为了解决MVC模式的controller层的缺点。MVC诞生的年代,手机app、网页等中的数据比较简单,没有现在这么复杂,因此那时候的数据解析处理很可能一步就解决了,MVC模式中的controller可以很轻松的胜任这些数据处理业务,但随着信息技术的发展,数据越来越庞大,数据处理的业务越来越复杂,此时的Controller显得十分臃肿。于是开发者们创建了一个新的类:ViewModel,专门用来做数据处理,这就是MVVM的由来。

    总结

    这次我们学习了什么是MVC,并通过自己实现一个简易的mvc框架让我们对MVC的设计思想有了更深的理解。MVC的思想核心在于,它将每个软件程序进行了分层(数据、视图、控制),这个模式的好处就是不论多么复杂的程序,从结构上来看都能将其在结构上分层,三层之间紧密联系,却又相互独立,每一层的变化都不会影响其他层,极大地提升了程序的可维护性和可扩展性。

    项目源码地址:github.com/channef/sim…

    终端指令:


    起源地下载网 » 从零实现简易MVC

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元