简单谈谈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
简单谈谈js中的MVC相关推荐
- html js脚本限制 正则,简单谈谈JS中的正则表达式
1.正则表达式包括两部分 ①定义正则表达式的规则: ②正则表达式的模式(i/g/m): 2.声明正则表达式 ① 字面量声明: var reg = /表达式规则/表达式模式: eg:var reg = ...
- 谈谈JS中的函数劫持
说到劫持,第一反应可能是什么不好的东西.函数劫持并不邪恶,关键是看使用的人.虽然这个概念在前端领域使用较少,但是在安全领域.自定义业务等场景下还是有一定的使用价值的.所以,这一篇文章将会和大家一起去了 ...
- 简单介绍js中的confirm()方法的使用
今天学习了js 中confirm的使用方法 confirm() 方法用于显示一个带有指定消息和 OK 及取消按钮的对话框. 如果用户点击确定按钮,则 confirm() 返回 true.如果点击取消按 ...
- 简单聊聊js中的内置对象
文章目录 一.Map对象 1.常用属性和方法 2.示例 二.Set 1.属性和方法 2.示例 三.Date对象 1.创建Date对象 1.1.*new Date() ;* 2.2.*new Date( ...
- mysql int()_简单谈谈MySQL中的int(m)
我们在设计表的时候,如果碰到需要设置int(整型)的时候,通常会按照惯例(大家都这样写)设置成int(11).那么这里为什么是11呢?代表的又是什么呢? 以前我一直以为这里是在限制int显示的宽度,后 ...
- python闭包的应用场景_简单谈谈Python中的闭包
Python中的闭包 前几天又有人留言,关于其中一个闭包和re.sub的使用不太清楚.我在脚本之家搜索了下,发现没有写过闭包相关的东西,所以决定总结一下,完善Python的内容. 1. 闭包的概念 首 ...
- android 代码中使用dp,简单谈谈Android中SP与DP的区别
从一开始写Android程序,就被告知这些常识 一.dp(或者dip device independent pixels) 一种基于屏幕密度的抽象单位.在每英寸160点的显示器上,1dp=1px.不同 ...
- [转] 谈谈JS中的函数节流
函数节流的目的 从字面上就可以理解,函数节流就是用来节流函数从而一定程度上优化性能的.例如,DOM 操作比起非DOM 交互需要更多的内存和CPU 时间.连续尝试进行过多的DOM 相关操作可能会导致浏览 ...
- android中sp的意义_简单谈谈Android中SP与DP的区别
从一开始写android程序,就被告知这些常识 一.dp(或者dip device independent pixels) 一种基于屏幕密度的抽象单位.在每英寸160点的显示器上,1dp=1px.不同 ...
最新文章
- Dos命令删除注册表项
- 原生js的ajax请求
- STM32中C语言知识点:初学者必看,老鸟复习(长文总结)
- 【JavaWeb】前端框架之Bootstrap
- c语言新龟兔赛跑_幽默 | 新龟兔赛跑
- 【2016年第3期】以大数据为核心 驱动智慧城市变革
- 系统架构----(2)大型网站架构之架构模式
- 三星Galaxy Z Fold3已开始量产:搭载骁龙888 Pro 售价或超2万
- Flutter基础—布局模型之层叠定位
- jvisualvm工具
- 里氏替换原则_趣谈设计模式之里氏替代原则
- 人工智能在5G网络中的应用
- vs2010和matlab混合编程,VS2010与Matlab2010b混合编程
- 浅述SaaS、CRM、OA、ERP、HR、进销存、财务系统的区别
- android 通知写法_Android消息通知-Notification
- 押对春节档《流浪地球》 阿里影业的专业进击之路
- 空间轨迹分析与应用(前言)Computing with Spatial Trajectories
- vtk读取CT序列mip投影
- 英语--日常生活名词收集 Daily Life Nouns Collection
- Mybatis——面试问题
热门文章
- !function(){}()
- [SharePoint][SharePoint2013循序渐进]SPS2013简介
- mysql中的字段类型
- ImageWatch的使用
- 卓金武——从数学建模到MATLAB
- python histo 改变 bins 大小_在Python中显示具有非常不均匀的bin宽度的直方图
- Wine下完美安装QQ 2010:
- lisp语言画阿基米德线_中国油画艺术表达着艺术家思想情感,而且展现了油画语言独特之美...
- vue使用echarts图表
- VScode前端开发常用插件