简单谈谈js中的MVC

MVC是什么?

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

本文将用一个经典的例子todoList来展开(代码在最后)。

 

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

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

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

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

 

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

1、hash驱动

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

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

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

 

一)模型

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

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

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

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

 

二)视图

视图是呈现给用户的,是用户交互的第一入口。它定义配置管理着每个页面相应的模板与组件,它表现一个模型的当前状态视图通过观察者模式监视模型,以获得最新的数据,来呈现最新的页面所以,页面首次加载时,往往是从接收模型的数据开始。

 

三)控制器

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

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

 

Demo-todoList

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

 

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

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

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

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

  1 <!DOCTYPE html>
  2 <html lang="en">
  3 <head>
  4     <meta charset="UTF-8">
  5     <title>todo</title>
  6 </head>
  7 <body>
  8 <header>
  9     <h3>待定事项</h3>
 10 </header>
 11 <main>
 12     <ul id="todoList"></ul>
 13     <input type="text" id="content">
 14     <button id="confirm">确认</button>
 15 </main>
 16 
 17 <script>
 18   (function () {
 19     const ADD_KEY = '__todoList__'
 20 
 21     const Utils = {
 22       // 模拟 Modal(实体模型)
 23       store(key, data) {
 24         if (arguments.length > 1) {
 25           return localStorage.setItem(key, JSON.stringify(data));
 26         } else {
 27           let storeData = localStorage.getItem(key);
 28           return (storeData && JSON.parse(storeData)) || []; // 这里一定要设置初始值为 []
 29         }
 30       }
 31     }
 32 
 33     class Todo {
 34       constructor(id, text = "") {
 35         this.id = id
 36         this.text = text
 37       }
 38     }
 39 
 40     let App = {
 41       init() {
 42         // this.todos 为一个存储json对象的数组, 是一个实例化的数据对象,可任意调用
 43         this.todos = Utils.store(ADD_KEY)
 44         this.findDom()
 45         this.bindEvent()
 46         this.render() // 初始化渲染
 47       },
 48 
 49 
 50       findDom() {
 51         this.contentBox = document.querySelector("#content")
 52         this.confirm = document.querySelector("#confirm")
 53         this.todoList = document.querySelector("#todoList")
 54         this.todoListItem = document.getElementsByTagName("li")
 55       },
 56 
 57       // 模拟 Controller (业务逻辑层)
 58       bindEvent() {
 59         this.confirm.addEventListener('click', () => {
 60           // 要求模型 M 改变状态,add()函数是写入数据操作
 61           this.add()
 62         }, false)
 63 
 64         this.todoList.addEventListener('click', (item) => {  // 事件委托,优化性能
 65           this.remove(item)
 66         }, false)
 67       },
 68 
 69       // 这里勉强抽象成一个视图吧!!!
 70       view() {
 71         let fragment = document.createDocumentFragment()   // 减少回流次数
 72         fragment = ''
 73 
 74         for (let i = 0; i < this.todos.length; i++) { // 一次性DOM节点生成
 75           // 这里使用拼接字符串代替视图的模板,
 76           // *******注意模板并不是一个视图,模板是由视图定义配置出来的,并被其管理着*******
 77           // 模板是用一种声明的方式指定部分甚至所有的视图对象
 78           fragment += `<li>${this.todos[i].text}</li>`
 79         }
 80         this.todoList.innerHTML = fragment
 81       },
 82 
 83       // render()函数作为一个订阅者的回调函数,数据的变化会反馈到模型 store
 84       // 换句话说:视图通过观察者模式,观察模型 store,当模型发生改变,触发视图更新
 85       render() {
 86         this.view()
 87 
 88         /**
 89          * 这里需要特别提一下,按照 MVC 原则这里本不应该出现下面的代码的
 90          * 因为业务逻辑关系(我本地存储使用的是同一个key值,再次写入数据会覆盖原来的数据,),
 91          * 所以必须通知模型 M 保存数据, V 层处理了不该它处理的逻辑,导致 M 与 V 耦合
 92          *
 93          * 解决办法是:将其抽象出来编写一个 视图助手 helper
 94          */
 95         Utils.store(ADD_KEY, this.todos)
 96       },
 97 
 98       getItemIndex(item) {
 99         let itemIndex
100         if (item.target.tagName.toLowerCase() === 'li') {
101           let arr = Array.prototype.slice.call(this.todoListItem)
102           let index = arr.indexOf(item.target)
103           return itemIndex = index
104         }
105       },
106 
107       add(e) {
108         let id = Number(new Date())
109         let text = this.contentBox.value
110         let addTodo = new Todo(id, text)
111         this.todos.unshift(addTodo) // 模型发生改变
112         this.render()  // 当模型发生改变,触发视图更新
113       },
114 
115       remove(item) {
116         let index = this.getItemIndex(item)
117         this.todos.splice(index, 1)
118         this.render()
119       }
120     }
121 
122     App.init()
123   })()
124 </script>
125 </body>
126 </html>

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

 

mvc模式的优点

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

 

mvc模式的弊端

1、清晰的构架以代码的复杂性为代价, 对小项目反而降低开发效率。 (如果本文的例子todoList用面条式代码编写,那得多简单啊!!!)
2、控制层和视图层耦合,导致没有真正分离和重用 

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

 

总结:其实,现在MVC在前端用得比较少了,因为它的局限性,催生了MVVM模式的流行与广泛使用,在下篇文章我会谈谈我对MVVM的理解,以及为何我使用基于MVVM模式的vue框架来高效开发。

转载于:https://www.cnblogs.com/LIUYANZUO/p/7231703.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/371654.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BTrace:Java开发人员工具箱中的隐藏宝石

这篇文章是关于BTrace的 &#xff0c;我正在考虑将其作为Java开发人员的隐藏宝藏。 BTrace是用于Java平台的安全&#xff0c;动态跟踪工具。 BTrace可用于动态跟踪正在运行的Java程序&#xff08;类似于DTrace&#xff0c;适用于OpenSolaris应用程序和OS&#xff09;。 不久&am…

Spring–添加SpringMVC –第2部分

在上一部分中&#xff0c;我们为经理和员工实现了控制器。 既然我们知道了解决方法&#xff0c;我们将做很少&#xff08;但仅做很少&#xff09;更复杂的事情–任务和时间表的控制器。 因此&#xff0c;让我们从org.timesheet.web开始。 TaskController 。 首先创建一个类&…

2016 Android Top 10 Library

过去的 2016 年&#xff0c;开源社区异常活跃&#xff0c;很多个人与公司争相开源自己的项目&#xff0c;让人眼花缭乱&#xff0c;然而有些项目只是昙花一现&#xff0c;有些项目却持久创造价值&#xff0c;为开发者提供了极大的便利&#xff0c;这些终究由时间来判断。今天&a…

您在eXo平台上的第一个Juzu Portlet

菊珠是佛教的佛珠。 一句话&#xff0c;我相信您已经学到了什么&#xff0c;印象深刻吗&#xff1f; 好的&#xff0c;我在这里不谈论佛教。 Juzu还是一个用于快速开发Portlet&#xff08;以及即将推出的独立应用程序&#xff09;的新框架。 您可以在Juzu网站上找到所需的所有…

Spring注入方式及注解配置

一&#xff1a;基于xml的DI&#xff08;Dependency Injection&#xff09; 注入类型&#xff1a; 定义学生Student实体类和小汽车Car实体类&#xff1a;进行封装和生成ToString(),并自定义属性Car Student 123456789101112131415161718192021222324252627282930313233343536373…

修改readonly属性的值

一般情况下&#xff0c;readonly属性的值是无法修改的&#xff0c;但可以通过特殊方式修改。定义一个student的类&#xff0c;其中name属性为readonly类型的变量 interface JFStudent : NSObjectproperty(nonatomic,copy,readonly) NSString *hisName;property(nonatomic,copy)…

ReactNative开发环境

此内容根据徐赢老师的文档整理后写处 原版地址&#xff1a;https://tuomaxu.gitbooks.io/reactnative/content/ ReactNative是跨平开发的解决方案&#xff0c;在开发平台的选择上&#xff0c;mac平台和win平台都可以。 所需要工具如下&#xff1a; 1&#xff0c;Nodejs环境 2&a…

MediaInfo源代码分析 1:整体结构

博客地址&#xff1a;http://blog.csdn.net/leixiaohua1020/article/details/12016231 MediaInfo源代码分析系列文章列表&#xff1a; MediaInfo源代码分析 1&#xff1a;整体结构MediaInfo源代码分析 2&#xff1a;API函数MediaInfo源代码分析 3&#xff1a;Open()函数MediaI…

射线碰撞检测

在我们的游戏开发过程中&#xff0c;有一个很重要的工作就是进行碰撞检测。例如在射击游戏中子弹是否击中敌人&#xff0c;在RPG游戏中是否捡到装备等等。在进行碰撞检测时&#xff0c;我们最常用的工具就是射线&#xff0c;Unity 3D的物理引擎也为我们提供了射线类以及相关的函…

php注册登录遍写入 遍验证,自动注册登录验证机制的php代码

在phpwind站点后台添加“广告管家”(CNZZ的一款广告投放的应用)的应用&#xff0c;整个“广告管家”通过iframe载入&#xff0c;载入的具体内容根据不同站点显示针对该站点的具体内容。出于意用性方面的考虑&#xff0c;需要以下二点&#xff1a;1、首次进入“广告管家”页面自…

Apache Wicket:记住我的功能

在Web应用程序中&#xff0c;具有“记住我”功能非常普遍&#xff0c;该功能使用户每次访问我们的网站时都能自动登录。 可以使用Spring Security来实现这种功能&#xff0c;但我认为将基于请求的身份验证框架与基于组件的Web框架结合使用并不是最好的主意。 这两个世界不能很好…

Ubuntu 安装中文

系统环境&#xff1a; 1. 官网 http://pinyin.sogou.com/linux/ 下载安装包。 2. 先运行 apt-get update 。 3. 再运行 apt-get -f install 。 4. 再运行 可能有的UBuntu系统自带了。 5. 如果下载的搜狐输入法安装包的格式为 .deb 的&#xff0c; 运行 &#xff1a; dpk…

JSF组件库–质量不只是零缺陷

自从我上次研究三个主要JSF组件库的质量以来&#xff0c;已经有一段时间了。 2009年12月&#xff0c;我开始比较RichFaces&#xff0c;Primefaces和ICEfaces的整体软件质量 。 从那时起&#xff0c;事情发生了变化&#xff0c;从现在开始&#xff0c;我想重新评估和更新它。 我…

字符串匹配(KMP 算法 含代码)

主要是针对字符串的匹配算法进行解说 有关字符串的基本知识传统的串匹配法模式匹配的一种改进算法KMP算法网上一比較易懂的解说小样例1计算next 2计算nextval代码有关字符串的基本知识 串&#xff08;string或字符串&#xff09;是由零个或多个字符组成的有限序列&#xff0c;一…

serialVersionUID的作用以及如何用idea自动生成实体类的serialVersionUID

转载&#xff1a;http://blog.csdn.net/liuzongl2012/article/details/45168585 serialVersionUID的作用&#xff1a; 通过判断实体类的serialVersionUID来验证版本一致性的。在进行反序列化时&#xff0c;JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVer…

JBoss BRMS最佳实践– BPM流程初始化层的提示

我过去发布过一些有关迁移策略的文章&#xff0c;仔细研究了流程层&#xff0c;并提供了一些有关jBPM的最佳实践 &#xff0c;它们都涉及到BPM策略的非常具体的部分。 我想重新讨论最佳实践的主题&#xff0c;然后在智能集成企业级别上&#xff0c;我们讨论使用JBoss BRMS对您的…

跨站点脚本(XSS)和预防

如OWASP网站&#xff08;https://www.owasp.org/index.php/Cross-site_Scripting_(XSS&#xff09;&#xff09;所述&#xff0c;跨站点脚本&#xff08;XSS&#xff09;攻击的变种几乎是无限的。 在这里&#xff0c;我建议使用基于Servlet筛选器的解决方案来清理HTTP请求。 攻…

NoSQL入门第一天——NoSQL入门与基本概述

一、课程大纲 二、入门概述 1.为什么用NoSQL 单机MySQL的年代&#xff1a; 一个网站的访问量一般都不大&#xff0c;用单个数据库完全可以轻松应付。      我们来看看数据存储的瓶颈是什么&#xff1f;        1.数据量的总大小 一个机器放不下时。&#xff08;现…

C语言结构体及函数传递数组參数演示样例

C语言结构体及函数传递数组參数演示样例 注&#xff1a;makeSphere()函数返回Sphere结构体&#xff0c;main函数中。调用makeSphere()函数&#xff0c;传递的第一个參数为数组&#xff0c;传递的数组作为指针。posted on 2017-07-30 18:42 mthoutai 阅读(...) 评论(...) 编辑 收…

AIX下RAC搭建 Oracle10G(六)dbca建库

AIX下RAC搭建系列 AIX下RAC搭建 Oracle10G&#xff08;六&#xff09;dbca建库 环境 节点 节点1 节点2 小机型号 IBM P-series 630 IBM P-series 630 主机名 AIX203 AIX204 交换机 SAN光纤交换机 存储 SAN T3存储 大纲流程例如以下&#xff1a; 第一部分&#xff1…