简单谈谈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,模拟一个存储数据对象的后台模型。
<!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.getElementsByTagName("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() /**
* 这里需要特别提一下,按照 MVC 原则这里本不应该出现下面的代码的
* 因为业务逻辑关系(我本地存储使用的是同一个key值,再次写入数据会覆盖原来的数据,),
* 所以必须通知模型 M 保存数据, V 层处理了不该它处理的逻辑,导致 M 与 V 耦合
*
* 解决办法是:将其抽象出来编写一个 视图助手 helper
*/
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、清晰的构架以代码的复杂性为代价, 对小项目反而降低开发效率。 (如果本文的例子todoList用面条式代码编写,那得多简单啊!!!)
2、控制层和视图层耦合,导致没有真正分离和重用
3、在同一业务逻辑下,如果存在多种视图呈现,需要视图定义配置多个模板引擎、数据解析,多次处理数据与页面更新。代码就充满了各种选择器与事件回调,随着业务的膨胀,变得难以维护。
总结:其实,现在MVC在前端用得比较少了,因为它的局限性,催生了MVVM模式的流行与广泛使用,在下篇文章我会谈谈我对MVVM的理解,以及为何我使用基于MVVM模式的vue框架来高效开发。
简单谈谈js中的MVC的更多相关文章
- 简单谈谈js中Promise的用法
首先先推荐一篇博文:http://blog.csdn.net/jasonzds/article/details/53717501 这篇博文很清晰的说明了Promise的用法,这里来简单总结一下: Pr ...
- 浅谈js中的MVC
MVC是什么? MVC是一种架构模式,它将应用抽象为3个部分:模型(数据).视图.控制器(分发器) 本文将用一个经典的例子todoList来展开 一个事件发生的过程(通信单向流动): 1.用户在视图V ...
- 简单谈谈Python中的几种常见的数据类型
简单谈谈Python中的几种常见的数据类型 计算机顾名思义就是可以做数学计算的机器,因此,计算机程序理所当然地可以处理各种数值.但是,计算机能处理的远不止数值,还可以处理文本.图形.音频.视频.网页等 ...
- 30 行代码实现 JS 中的 MVC
一连串的名字走马观花式的出现和更迭,它们中一些已经渐渐淡出了大家的视野,一些还在迅速茁壮成长,一些则已经在特定的生态环境中独当一面舍我其谁.但不论如何,MVC已经并将持续深刻地影响前端工程师们的思维方 ...
- 简单聊一聊JS中的循环引用及问题
本文主要从 JS 中为什么会出现循环引用,垃圾回收策略中引用计数为什么有很大的问题,以及循环引用时的对象在使用 JSON.stringify 时为什么会报错,怎样解决这个问题简单谈谈自己的一些理解. ...
- 谈谈JS中的高级函数
博客原文地址:Claiyre的个人博客如需转载,请在文章开头注明原文地址 在JavaScript中,函数的功能十分强大.它们是第一类对象,也可以作为另一个对象的方法,还可以作为参数传入另一个函数,不仅 ...
- 谈谈js中for in 需要注意的地方
js中for in 可以遍历对象或数组的显性属性,也就是说我们自己定义的属性是可以遍历的,那些原型上默认已有的属性,例如:Object.prototype.toString.Object.protot ...
- 如何简单理解js中this的指向
前序 每个人学js都会被this指向这个东西搞得很蒙,那就是this的指向问题.首先,我们要明白 this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上thi ...
- 谈谈JS中的原型
不知道大家对JS中的原型理解的怎么样,我想如果大家对JS中的原型对象以及prototype属性十分熟悉的话对后面原型链以及继承的理解会十分的容易,这里想和大家分享自己对其的理解,请先看下面这段代码O( ...
随机推荐
- [转] DDD领域驱动设计框架分享
从去年10月份开始,学了几个月的领域驱动设计(Domain Driven Design,简称DDD).主要是学习领域驱动设计之父Eric Evans的名著:<Domain-driven desi ...
- Perl正则表达式例子
Perl正则表达式 一.介绍 正则表达式各语言都有自己的规范,但是基本都差不多,都是由元字符的组合来进行匹配:由于Nmap内嵌的服务与版本探测是使用的Perl正则规范,因此此篇博客记录一下Perl正则 ...
- 下载旧版chrome
问题描述: xp只能使用chrome 49及其之前的版本,去哪里下载? 解决办法: 1. 在这里 http://www.slimjet.com/chrome/google-chrome-old-ver ...
- 自定义分布式RESTful API鉴权机制
微软利用OAuth2为RESTful API提供了完整的鉴权机制,但是可能微软保姆做的太完整了,在这个机制中指定了数据持久化的方法是用EF,而且对于用户.权限等已经进行了封装,对于系统中已经有了自己的 ...
- 【转载】Sublime Text 3065 Keygen and Patcher
原始日期:2014-10-01 18:25 差不多时隔一年了,Sublime Text 终于更新啦!相信很多友友都已经升级到3065版本了,所以我也特地抽空为大家做了个新版补丁.该补丁仅作为 ...
- 用nrm一键切换npm源
当使用官方npm源安装各种包比较慢的时候,建议修改npm源地址 查看npm源地址,在终端输入以下命令 npm config list 会看到官方的npm源 metrics-registry = &qu ...
- Swift字符串可变性
您可以通过将一个特定字符串分配给一个变量来对其进行修改,或者分配给一个常量来保证其不会被修改: var variableString = "Horse" variableStrin ...
- MATLAB命令大全和矩阵操作大全
转载自: http://blog.csdn.net/dengjianqiang2011/article/details/8753807 MATLAB矩阵操作大全 一.矩阵的表示在MATLAB中创建矩阵 ...
- Swift 路由机制设计
设计模式 APP设计模式多种多样,从最初的MVC到MVVM,再到MVP,VIPER等.越来越多的设计模式被开发出来并得以应用,但不论我们用到哪种设计模式,只需要记住高内聚.低耦合那边是好的设计模式.在 ...
- 4.如何实现用MTQQ通过服务器实现订阅者和发布者的通讯
1.本例子意在用moquette服务器来作为消息转发,通过订阅者订阅消息,发布者发布消息,然后发布者的消息可以通过服务器转发给订阅者 服务器例子: https://github.com/andsel/ ...