zoukankan      html  css  js  c++  java
  • 浅谈js中的MVC

    MVC是什么?

    MVC是一种架构模式,它将应用抽象为3个部分:模型(数据)、视图、控制器(分发器)

    本文将用一个经典的例子todoList来展开

    一个事件发生的过程(通信单向流动):

    1、用户在视图V上与应用程序交互

    2、控制器C触发相应的事件,要求模型M改变状态(读写数据)

    3、模型M将数据发送到视图V,更新数据,展现给用户

    在js的传统开发模式中,大多基于事件驱动的:

    1、hash驱动

    2、DOM事件,用来驱动视图

    3、模型事件(业务模型事件和数据模型事件),用来驱动模型和模型结合

    所以js中的MVC的特点是:单向流动、事件驱动

    一)模型

    模型存放着应用的所有数据对象(业务数据、数据校验、增删改查),比如,例子todoList中的store模型,存放每一条记录与之有关的逻辑

    数据时面向对象的,当控制器请求模型读写数据时,模型就将数据包装成模型实例。任何定义在这个数据模型上的函数或逻辑都可以直接被调用。在本文的例子中采用localStorage也是类似道理的。存储的Todos可以随时被调用

    模型不关心,不包含视图和控制器的逻辑。它们应该是相互解耦的。这里提一点,模型与视图的耦合,显然是违反MVC架构原则,但往往我们有时候却因为业务关系而无法完全解耦

    模型表现了领域特定的数据,当一个模型有所改变的时候,它会通知它的观察者

    二)视图

    视图是呈现给用户的,是用户交互的第一入口。它定义配置、管理者每个页面相应的模板与组件,它表现为一个模型的当前状态,视图通过观察者模式监视模型,以获得最新的数据,来呈现最新的页面。

    所以,页面首次加载时,往往是从接受模型的数据开始

    三)控制器

    控制器(分发器),是模型和视图之间的桥梁,集中式地配置和管理事件分发、模型分发、视图分发,还用来权限控制、异常处理等。我们的应用中往往是有多个控制器的

    页面加载完成后,控制器会监听视图的用户交互(按钮点击或表单提交),一旦用户发生交互时,控制器做出视图的选择,触发控制器的事件处理机制,去派发新的事件,通知模型更新数据

    Demo-todoList


    最后这里是一个用原生js写的todoList,这个demo做的很简陋,点击输入文字点击确定就添加,删除是直接点击该行信息

    单独分离开来举例子不好讲,所以在代码中进行注释。首先简单理下下边代码的思路:

    1、V层定义配置了一个显示数据的字符串模板,同时定义一个订阅者的回调函数render()用于页面更新数据

    2、C层监听用户的添加与删除操作,添加是add()函数,他执行了回调函数render,同时向M层写入数据,通知M层改变,删除操作同理

    3、M层是本地存储localStorage,模拟一个存储数据对象的后台模型

    <!DOCTYPE html>
    <html lang="en">
    <head>
               <meta charset="UTF-8">
                <title>todo</title>
    </head>
    <body>
    <header>
                <h3>待定事项</h3>
    </header>
    <main>
               <ul id="todoList"></ul>
               <input type="text" id="content">
               <button id="confirm">确认</button>
    </main>
    <script>
        (function(){
                      const ADD_KEY='_todoList_'
                      const Utils={
                       //模拟Modal(实体模型)
                   store(key,data)
                    if(arguments.length>1){
                      return localStorage.setItem(key,JSON.stringify(data));
    }
    else{
            let storeData=localStorage.getItem(key);
            return (storeData &&JSON.parse(storeData))||[];//这里一定要设置初始值为[]
    }
    }
    }
    class Todo{
              constructor(id,text=""){
                      this.id=id;
                     this.text=text
    }
    }
               let App={
                    init(){
                           //this.todos为一个存储json对象的数组,是一个实例化的数据对象,可任意调用
                      this.todos=Utils.store(ADD_KEY)
                      this.findDom()
                      this.bindEvent()
                      this.render()//初始化渲染
            },
                         findDom(){
                    this.contentBox=document.querySelector("#content")
    this.confirm=document.querySelector("#confirm")
    this.todoList=document.querySelector("#todoList")
    this.todoListItem=document.getElementByTagName("li")
    },
    //模拟Controller(业务逻辑层)
    bindEvent(){
             this.confirm.addEventListener('click',()=>{
             //要求模型M改变状态,add()函数是写入数据操作
             this.add()
     },false)
               this.todoList.addEventListener('click',(item)=>{//时间委托,优化性能 
              this.remove(item)
    },false)
    },
    //这里勉强抽象成一个视图吧
    view(){
    let fragment=document.createDocumentFragment()//减少回流次数
    fragment=''
    for(let i=0;i<this.todos.length;i++){ //一次性DOM节点生成
    //这里使用拼接字符串代替视图的模板,
    //模板是用一种声明的方式指定部分甚至所有的视图对象
    fragment +='<li>&{this.todos[i].text}</li>
    }
    this.todoList.innerHTML=fragment
    },
    //render()函数作为一个订阅者的回调函数,数据的变化会反馈到模型store
    //换句话说:视图通过观察者模式,观察模型store,当模型发生改变,触发视图更新
    render(){
       this.view()
    Utils.store(ADD_KEY,this.todos)
    },
    getItemIndex(item){
    let itemIndex
    if (item.target.tagName.toLowerCase()==='li'){
    let arr=Array.prototype.slice.call(this.todoListItem)
    let index=arr.indexOf(item.target)
    return itemIndex=index
    }
    },
    add(e){
     let id=Number(new Date())
    let text=this.contentBox.value
    let addTodo=new Todo(id,text)
    this.todos.unshift(addTodo)//模型发生改变
    this.render()//当模型发生改变,触发视图更新
    },
    remove(item){
    let index=this.getItemIndex(item)
    this.todos.splice(index,1)
    this.render()
    }
    }
    App.init()
    })()
    </script>
    </body>
    </html>

    随着界面和逻辑的复杂,用js或者jq去控制DOM是不现实的。上边例子只是用原生js模拟MVC的思想实现过程。真正地项目往往会依赖一些封装好的优秀库进行高效开发

    MVC模式的优点

    MVC编程把所有精力放在数据处理,尽可能减少对网页元素的处理。对于有一定数量功能的网页,MVC模式下强制规范代码,简化,减少重复代码,使代码易于扩充

    MVC模式的弊端

    1、清晰的构架以代码的复杂性为代价,对小项目反而降低开发效率

    2、控制层和视图层耦合,导致没有真正分离和重用

    3、在同一业务逻辑下,如果存在多种视图呈现,需要视图定义配置多个模板引擎、数据解析,多次处理数据与页面更新。代码就充满了各种选择器与事件回调,随着业务的膨胀,变得难以维护

    总结:其实,现在MVC在前端用得比较少了,因为它的局限性,催生了MVVM模式的流行与广泛使用

  • 相关阅读:
    The network bridge on device VMnet0 is not running
    QuickContactBadge去掉三角
    在Android Studio中调用so中的方法
    Android Studio动态调试smali代码
    用AndroidStudio创建so
    Android逆向 破解第一个Android程序
    Java配置----JDK开发环境搭建及环境变量配置
    AndroidKiller报.smali文件丢失问题解决(关闭Android Studio的Instant Run)
    Android逆向 Android平台虚拟机
    Android逆向 APK文件组成
  • 原文地址:https://www.cnblogs.com/zzp0320/p/7233610.html
Copyright © 2011-2022 走看看