MVC是什么?
MVC是一种架构设计模式,它的核心是关注点的分离,即将模型、视图、控制器分离开来。
M模型、V视图、C控制器
- 模型model封装了与业务逻辑相关的数据以及对数据的处理方法,即负责操作所有数据
- 视图view是它在屏幕上的表示,描绘的是model的当前状态。即负责所有 UI 界面
- 控制器controller定义用户界面对用户输入的响应方式,起到不同层面间的组织作用,用于控制应用程序的流程,它处理用户的行为和数据model上的改变。即负责M、V之外的其他事情
经典MVC模型图
实现一个简易MVC
为了更好地理解MVC模式,我们来用js实现一个简易的mvc。创建一个文件夹,目录结构如下:
mvc-demo
|-- index.html
|-- main.js
|-- app.js
|-- src
|-- model.js
|-- view.js
|-- controller.js
使用 npm init -y 命令初始化项目,此命令需要已安装nodejs。
M(modle层)
在src/mvc/model.js中定义一个模型类,该类拥有增删改查数据的方法,并通过new实例时初始化数据。
class Model {
// 数据模型初始化:绑定数据
constructor(data) {
this._data=data
}
// 提供数据增删改查操作
create () { }
delete () { }
update (data) {this._data=data}
get () { return this._data }
}
export default Model
V(view层)
在src/mvc/view.js中定义一个视图类,提供视图更新操作,new实例时初始化页面设置(绑定页面元素和html模板)
class View {
// 视图初始化
constructor(el,template) {
this.el = document.querySelector(el)
this.template = template
}
render () { } // 渲染视图
}
export default View
C(controller层)
在src/mvc/controller.js中定义一个控制器类,将上述的M、V层组织起来,进行事件绑定、视图更新渲染,处理相关逻辑。
class Controller{
constructor(model, view, methods) {
this.model = model
this.view = view
this.methods=methods
this.init()
this.bindMethods()
}
// 初始化方法
init () {
// 实现视图渲染方法
this.view.render = () => { this.view.el.innerHTML = this.view.template.replace('{{data}}',this.model.get()) }
this.view.render() //渲染视图
}
// 绑定按钮事件
bindMethods () {
[].forEach.call(this.methods, element => {
// 采用事件委托的方式绑定事件到容器元素上,防止视图重新渲染时子元素被替换导致监听事件失效
this.view.el.addEventListener('click', (e) => {
if (e.target.id === element.DomID)
{
element.fn() // 执行对应方法
this.view.render() // 重新渲染视图
}
})
})
}
}
export default Controller
使用这个简易的MVC
简单起见,我们实现一个四则运算计算器,页面上只提供数据和四个加减乘除操作按钮。 src/index.html内容如下
<body>
<div id="app"></div>
<script src='./main.js'></script>
</body>
在src/main.js中导入这几个类,写好业务逻辑和相关配置,实现他们。
import Model from "./mvc/model"
import View from "./mvc/View"
import Controller from "./mvc/controller"
const options = {
data: 100,
el:'#app',
template: `<div>{{data}}<div>
<div><button id="add">+1</button></div>
<div><button id="sub">-1</button></div>
<div><button id="mul">x10</button></div>
<div><button id="div">/10</button></div>
`,
methods: [
{
DomID: 'add',
fn: () => { model.update(model.get()+1) }
},
{
DomID: 'sub',
fn: () => { model.update(model.get()-1) }
},
{
DomID: 'mul',
fn: () => { model.update(model.get()*10) }
},
{
DomID: 'div',
fn: () => { model.update(model.get()/10) }
}
]
}
const { data, el, template, methods }=options
const model = new Model(data)
const view = new View(el, template)
const controller = new Controller(model, view, methods)
试试效果
优化封装
我们已经成功地实现了一个简易mvc框架,并成功使用它制作了一个小demo,但这个框架还有诸多可以改善的地方。比如代码封装不够简洁,如果要再写一个demo,又得重新写一大串代码,能不能让我只配置相关的数据和方法就好呢?接下来我们开始优化。
再加一层?
软件工程中有这么一句话:没有什么是加一层解决不了的。接下来我们加一层app.js来实现一个mvc具体实现代码的封装,依据最小知识原则,我们在使用app.js创建项目时应该只需要传最少的配置,因此我们来实现一个app实现类。 在src/app.js中实现App类,该类通过构造函数传入配置参数,自动帮我们实例化了mvc每个类的操作,使得我们调用这个类时只需要传一个options参数即可。
import Model from "./mvc/model"
import View from "./mvc/View"
import Controller from "./mvc/controller"
class App{
constructor({ data, el, template, methods }) {
this.model = new Model(data)
this.view = new View(el, template)
this.controller = new Controller(this.model, this.view, methods)
}
}
export default App
在src/main.js中引入app.js并配置相关数据参数。由于此时的methods已经无法直接调用model类,因此写成function函数,用this代替,并在controller添加监听事件时绑定this到model。
import App from './app'
new App({
data: 100,
el:'#app',
template: `<div>{{data}}<div>
<div><button id="add">+1</button></div>
<div><button id="sub">-1</button></div>
<div><button id="mul">x10</button></div>
<div><button id="div">/10</button></div>
`,
methods: [
{
DomID: 'add',
fn: function () { this.model.update(this.model.get()+1) }
},
{
DomID: 'sub',
fn: function () { this.model.update(this.model.get()-1) }
},
{
DomID: 'mul',
fn: function () { this.model.update(this.model.get()*10) }
},
{
DomID: 'div',
fn: function () { this.model.update(this.model.get()/10) }
}
]
})
controller.js修改内容:
// 绑定按钮事件
bindMethods () {
[].forEach.call(this.methods, element => {
// 采用事件委托的方式绑定事件到容器元素上,防止视图重新渲染时子元素被替换导致监听事件失效
this.view.el.addEventListener('click', (e) => {
if (e.target.id === element.DomID)
{
element.fn.call(this,arguments) // 执行对应方法
this.view.render() // 重新渲染视图
}
})
})
}
之后如果想再写一个新的demo,就可以直接复用app.js,简单的传入一些参数:容器元素、数据、绑定事件,就搞定啦,是不是很简洁?
有点像Vue?
app.js封装之后,引入app并传配置的步骤怎么越看越像Vue呢?其实,Vue的MVVM思想就是在MVC的基础之上发展而来的。我们先来看一下MVVM的特点。
M(Model模型) MVVM中的M保存的是每个页面中单独的数据
VM(ViewModel) 它是一个调度者,分割了M和V每当V想要获取后面保存数据的时候,都要由VM做中间的处理
V(view视图) 就是每个页面中的HTML结构
VM是MVVM的思想核心,是 M 和 V 之间的调度者,数据的双向绑定是由 VM 提供的; MVVM与MVC最大的区别就是:MVVM实现了View和Model的自动同步,不用再自己手动操作Dom元素了,即Model变化时View可以实时更新,View变化也能让Model改变。 因此理解了MVC,我们就理解了Vue的设计思路,因为他们本质上都是一样的,都是通过分离关注点的设计,将代码抽象隔离开来。
MVC、MVVM傻傻分不清楚?
MVVM的出现是为了解决MVC模式的controller层的缺点。MVC诞生的年代,手机app、网页等中的数据比较简单,没有现在这么复杂,因此那时候的数据解析处理很可能一步就解决了,MVC模式中的controller可以很轻松的胜任这些数据处理业务,但随着信息技术的发展,数据越来越庞大,数据处理的业务越来越复杂,此时的Controller显得十分臃肿。于是开发者们创建了一个新的类:ViewModel,专门用来做数据处理,这就是MVVM的由来。
总结
这次我们学习了什么是MVC,并通过自己实现一个简易的mvc框架让我们对MVC的设计思想有了更深的理解。MVC的思想核心在于,它将每个软件程序进行了分层(数据、视图、控制),这个模式的好处就是不论多么复杂的程序,从结构上来看都能将其在结构上分层,三层之间紧密联系,却又相互独立,每一层的变化都不会影响其他层,极大地提升了程序的可维护性和可扩展性。
项目源码地址:github.com/channef/sim…
终端指令:
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!