浅谈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模式的流行与广泛使用

时间: 2024-10-07 18:56:24

浅谈js中的MVC的相关文章

浅谈js中的this关键字

浅谈js中的this关键字 全局作用域中的this 函数作用域中的this 不同函数调用方法下的this 直接调用 作为对象的方法调用 作为构造函数调用 通过call或apply方法调用 嵌套函数作用域中的this 浅谈js中的this关键字 this是JavaScript中的关键字之一,在编写程序的时候经常会用到,正确的理解和使用关键字this尤为重要.接下来,笔者就从作用域的角度粗谈下自己对this关键字的理解,希望能给到大家一些启示,权当交流之用. 全局作用域中的this 本文将以作用域由

浅谈js中arguments对象

一.先来看看到底是个啥? arguments对象在JS中应该算数比较特殊的对象.不能显示的创建,只在当前函数调用的时候才可以使用,长得有点像数组,但绝对不是Array的实例. 几点说明: 1.1.arguments实际上是当前函数的一个内置属性,在当前函数内,函数名.arguments等价于arguments,在函数外单独使用arguments是没有意义的; 1.2.arguments对象的长度由实际参数个数决定.1.3.函数中的形参和实参是可以相互影响的. 可以看出,当传入的实参个数少于形参个

浅谈js中继承的理解和实现

一.前言 java.C#等正统面向对象语言都会提供类似extend之类的处理类的继承的方法,而javascript并没有提供专门的方法用于继承,在javascript中使用继承需要一点技巧.js中实例的属性和行为是由构造函数和原型两部分组成的,js的继承也分为这两部分.下面给大家分享一下在js中如何实现继承,讲的不对的地方望大家指正! 二.继承构造函数中的属性和行为 我们定义两个类Animal和Bird类,来实现js中类的继承. //定义Animal类 function Animal(name)

浅谈js中的九个算法

排序很多时候都会用到,而在js中排序的算法有九个是人们常用的,而且使用起来可以很流畅.本文将对这九种排序算法进行详细介绍,教程尚硅谷JavaScript DOM视频教程还有详细的代码分享哦. 一.插入排序 1)算法简介插 入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法.它的工作原理是 通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入.插入排序在实现上,通常采用in-place排序(即只需用到 O(1)的额外空间的排序),因而在从后向前扫描

浅谈js中的数据类型,使用typeof获取js数据类型

JS中的数据类型 1):Undefined——值未定义 注:Undefined类型只有一个值,即特色的undefined.在使用var声明变量但未对其加以初始化时,这个变量的值就是undefined 2):boolean——布尔值 注:true和false是区分大小写的.也就是说,True和False(以及其他的混合大小写形式)都不是Boolean值,只是标识符 3):string——字符串 注:用双引号和单引号表示的字符完全相同 4):number——数值 注:表示整数和浮点数值 5):Nul

浅谈JS中的闭包

今天 大年初一,祝各位小伙伴们狗年旺旺啊,闲来也没事,只能钻研一下自己的分内之事,也就是作为一个前端码农的身份,得时刻保持学习的态度,温故而知新,每天都给自己一个小目标去完成,日积月累,所想达到的状态,都会有所见长. JS中的闭包,想必,做web开发的程序猿们都有一定的认识吧,不仅仅是js里有这种特性,而且弱语言类型的譬如python里也是有闭包这么一个强大的特性的,对于老司机们来说,闭包可真的是一个很好的东西,但是对于新入门的菜鸟们来说,闭包却是一个难以搞得清楚的特性,很多情况下,会用,但是却

浅谈JS中的!=、== 、!==、===的用法和区别 JS中Null与Undefined的区别 读取XML文件 获取路径的方式 C#中Cookie,Session,Application的用法与区别? c#反射 抽象工厂

var num = 1;    var str = '1';    var test = 1;    test == num  //true 相同类型 相同值    test === num //true 相同类型 相同值    test !== num //false test与num类型相同,其值也相同, 非运算肯定是false    num == str  //true 把str转换为数字,检查其是否相等.    num != str  //false == 的 非运算    num ==

浅谈JS中的typeof和instanceof的区别

JS中的typeof和instanceof常用来判断一个变量是否为空,或者是什么类型. typeof typeof运算符返回一个用来表示表达式的数据类型的字符串. typeof一般返回以下几个字符串: "number", "string","boolean","object","function","undefined" 对于Array,Null等特殊对象使用typeof一律返回obje

浅谈JS中 reduce() 的用法

过去有很长一段时间,我一直很难理解 reduce() 这个方法的具体用法,平时也很少用到它.事实上,如果你能真正了解它的话,其实在很多地方我们都可以用得上,那么今天我们就来简单聊聊JS中 reduce() 的用法. 一.语法 arr.reduce(function(prev,cur,index,arr){ ... }, init); 其中, arr 表示原数组: prev 表示上一次调用回调时的返回值,或者初始值 init; cur 表示当前正在处理的数组元素: index 表示当前正在处理的数