什么是MVC?
MVC是一种架构模式,它将应用程序抽象为三个部分:模型(数据)、视图和控制器(分发器)。
本文将以一个经典的例子todoList展开(代码在最后)。
事件的过程(单向通信流):
1.用户在视图v上与应用程序交互。
2.控制器C触发相应的事件,要求模型M改变其状态(读写数据)
3.模型M将数据发送到视图V,更新数据并呈现给用户
在js的传统开发模式中,大多数都是基于事件驱动的:
1.哈希驱动器
2.DOM事件,用于驱动视图
3.模型事件(业务模型事件和数据模型事件)用于驱动模型和模型的组合
因此,js中mvc的特点是:单向流和事件驱动
1)模型
该模型存储应用程序的所有数据对象(业务数据、数据验证、添加、删除、修改和查询),例如,todoList中的存储模型存储每个记录及其相关逻辑。
数据是面向对象的。当控制器请求模型读写数据时,模型将数据包打包到模型实例中。可以直接调用在这个数据模型上定义的任何函数或逻辑。在这个例子中,它类似于使用localSrorage。存储的Todos可以随时调用
模型不在乎,它不包含视图和控制器的逻辑。它们应该相互分离。这里需要提到的是,模型和视图的耦合显然违背了MVC架构原则,但有时我们也不能因为业务关系而完全解耦
模型代表特定领域的数据,当模型改变时,它会通知它的观察者(视图)。
2)观点
视图呈现给用户,并且是用户交互的第一个条目。它定义、配置和管理每个页面的相应模板和组件。它表示模型的当前状态。该视图通过观察者模式监控模型,以获取最新数据并呈现最新页面。因此,当第一次加载一个页面时,它通常从接收模型的数据开始。
3)控制器
控制器(分发器)是模型和视图之间的桥梁,集中配置和管理事件分发、模型分发和视图分发,还用于权限控制和异常处理。我们的应用程序中经常有多个控制器
页面加载后,控制器将监控视图的用户交互(按钮点击或表单提交)。一旦用户交互,控制器将选择视图,触发控制器的事件处理机制,分派新事件,并通知模型更新数据(从而返回到第一步)
演示-todoList
最后,这里有一个用原生js编写的todoLIst。这个演示非常简单。单击输入文本,然后单击确定添加它。要删除它,请直接单击这一行信息。
单独的例子很难区分,所以在代码中做注释。首先,简单梳理一下下面代码的思路:
1.V层定义配置了显示数据的字符串模板,定义了订阅者的回调函数render(),用于更新页面上的数据。
2.C层监控用户的添加和删除操作。Add是一个add()函数,它执行回调函数render并将数据写入M层,以通知M层发生了变化。删除操作是一样的。
3.M层是localStorage,它模拟了一个存储数据对象的后台模型。
!doctype html lang=' en ' head meta charset=' utf-8 ' title todo/title/header dyheader H3待定/H3/header main ul id=' todoList '/Ulinput type=' text ' id=' content ' button id=' confirm ' confirm/button/mainscript(function(){ const add _ key=' _ _ todoList _ _ ' consutils={//仿真Modal(实体模型)存储区(key,data){ if(arguments . length 1){返回本地存储区. setitem (key,data)。} else { let storeData=local storage . getitem(key);return(storeData JSON . parse(storeData))| |[];//初始值必须设置为[]} } } class Todo {构造函数(id,Text='') {this。id=idthis。text=text}}让app={init () {//this。todos是一个用于存储json对象和实例化数据对象的数组。你可以称之为。todos=utils.store (add _ key) this。finddom()这个。bindeevent()这个。render()//随意初始化呈现}。find DOM(){ this . content box=document . queryselector(# content)this . confirm=document . queryselector(# confirm)this . todoList=document . queryselector(# todoList)this . todolistitem=document . getelementsbytagname(' Li ')},//模拟Controller(业务逻辑层)bind event () {this。确认。addeventlistener ('click ',()={//需要模型m改变状态,add()函数是数据写操作this.add()}。没错)这个。托多里斯。addeventlistener ('click ',(item)={//事件委托,优化性能this.remove(item) },false) },//我们在这里抽象成一个视图!view(){ let fragment=document . createdocumentfragment()//减少(let I=0;I this . todos . length;I) {//一次性生成DOM节点//这里用拼接字符串代替视图模板。//* * * * * * * *请注意,模板不是视图,它们是由视图定义配置的。并由它管理。* * * * * *//模板指定了部分甚至全部视图对象片段=` Li $ {this。托多斯[我]。文}/李` }这个。托多斯。innerhtml=fragment}、//render()函数作为订阅服务器的回调函数。数据变化会反馈到模型存储//换句话说,视图会通过观察者模式观察模型存储,当模型发生变化时,会触发视图更新render() {this.view() /** *,这里需要特别提一下。根据MVC原理,下面的代码不应该出现在这里*因为业务逻辑关系(我在本地存储中使用相同的键值,再次写入数据会覆盖原始数据),*所以我必须通知模型M保存数据,V层处理它不应该处理的逻辑。m和v之间耦合的* *解决方案是对其进行抽象,并编写一个view assistant helper */utils . store(add _ key,this。todos)},getiteindex(item){ let item index if(item . target . tagname . tolowercase()==' Li '){ let arr=array . prototype . slice . call(this . todolistitem)let index=arr . indexof(item . target)返回itemIndex=index } },add(e){ let id=Number(new Date())let Text=this . content box . value let addTodo=new Todo(id,Text) this。todos . unshift(add todo)//模型改变this.render() //当模型改变时,会触发视图更新},Remove (item) {let index=this。getitemindex (item)此。托多斯。拆分(索引,1)这个。render ()}}应用程序。init()})))/script/body/html随着接口和逻辑的复杂,用js或者jq来控制DOM是不现实的。上面的例子只是用原生js模拟了mvc的思想实现过程。真正的项目通常依赖于一些打包的优秀库来实现高效开发。
mvc模式的优势
Mvc编程将全部精力放在数据处理上,并最大限度地减少了对网页元素的处理。对于具有一定数量功能的网页,Mvc模式强制代码标准化、简化、减少重复代码,并使代码易于扩展。
mvc模式的缺点
1.一个清晰的框架会降低小项目的开发效率,代价是代码的复杂性。(如果本文中的todoList例子是用面条代码写的,那该有多简单!2.控制层和视图层是耦合的,导致没有真正的分离和重用
3.在同一个业务逻辑下,如果有多个视图,需要配置多个模板引擎进行视图定义,分析数据,多次处理数据和页面更新。代码中充满了选择器和事件回调,随着业务的扩展,这些变得难以维护。
总结:其实MVC很少在前端使用,因为它的局限性,催生了MVVM模式的流行和广泛使用。在下一篇文章中,我将谈谈我对MVVM的理解,以及我为什么使用基于MVVM模式的vue框架来实现高效开发。
上面提到的js中的MVC就是边肖分享的所有内容。希望能给大家一个参考,多支持我们。