MVC

- 视图 (View):用户界面
- 控制器 (Controller):业务逻辑
- 模型 (Model):数据保存,渲染数据源
例如:
- view 收到dom点击指令到 Controller;
- Controller 完成业务逻辑后,要求Modal改变数据状态;
- Modal将新数据发送到View,用户得到反馈;
代码实现以Backbone.js 计数器示例:
html
<!DOCTYPE html>
<html>
<head>
<title>Backbone MVC Counter</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.13.1/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.4.0/backbone-min.js"></script>
</head>
<body>
<div id="app">
<div>count: <span id="count-display">0</span></div>
<button id="increment-btn">加+1</button>
</div>
<script>
// 1. Model (数据 + 逻辑)
const CounterModel = Backbone.Model.extend({
defaults: {
count: 0
},
increment: function() {
this.set({ count: this.get("count") + 1 }); // 更新数据
}
});
// 2. View (UI + 事件监听)
const CounterView = Backbone.View.extend({
el: "#app", // 挂载点
events: {
"click #increment-btn": "handleIncrement" // 监听按钮点击
},
initialize: function() {
this.model = new CounterModel(); // 初始化 Model
this.listenTo(this.model, "change", this.render); // 监听 Model 变化
this.render(); // 初始渲染
},
handleIncrement: function() {
this.model.increment(); // 调用 Model 方法修改数据
},
render: function() {
// 手动更新 DOM
this.$("#count-display").text(this.model.get("count"));
return this;
}
});
// 启动应用
new CounterView();
</script>
</body>
</html>
MVC 数据流(Backbone.js)
- 用户点击按钮 →
View
捕获事件,调用handleIncrement
。 - View 调用 Model 方法 →
increment()
修改数据。 - Model 触发
change
事件 →View
监听到变化,手动更新 DOM(render()
)。
特点:
- 手动 DOM 更新 (
render()
方法)。 - Model 和 View 解耦,但需要显式监听数据变化。
- Controller 逻辑分散在 View 中(Backbone 没有严格独立的 Controller)。
MVVM
MVVM出现原因解决:
分离关注点,分离视图与业务逻辑
-
MVC中需要手动控制View和Model之间的双向数据绑定的繁琐逻辑,如上Backbone.js的示例,Controller分散在View中代码耦合度高,维护困难,测试困难;
-
MVVM引入视图模型(ViewModel)自动双向数据绑定,即修改定义的响应数据(
vue:ref
;react:state
),自动反应到数据在视图上的变化;让开发者只需要关注业务逻辑,不需要关心view和Model的绑定关系,由于ViewModel(业务处理数据逻辑)与View分离,可能独立于视图测试,提高测试效率。
Vue (MVVM 模式)
在 MVVM 中:
- Model :
ref
/reactive
数据。 - View :模板(
<template>
)。 - ViewModel :
<script setup>
中的逻辑(处理数据绑定和事件)。
代码实现
javascript
<script setup>
import { ref } from 'vue'
// Model (数据)
const count = ref(0)
// ViewModel (逻辑)
const handleIncrement = () => {
count.value += 1 // 修改数据,Vue 自动更新视图
}
</script>
<!-- View (模板) -->
<template>
<div>count: {{ count }}</div>
<button @click="handleIncrement">加+1</button>
</template>
MVVM 数据流(Vue)
- 用户点击按钮 →
View
触发@click
事件。 - ViewModel 执行
handleIncrement
→ 修改count
。 - Vue 自动检测数据变化 → 更新 DOM(无需手动操作)。
特点:
- 自动 DOM 更新(响应式系统)。
- 双向绑定 (表单输入可用
v-model
)。 - ViewModel 作为中间层,解耦 Model 和 View。
MVC vs. MVVM 对比
特性 | Backbone.js (MVC) | Vue (MVVM) |
---|---|---|
数据绑定 | 手动更新 DOM (render() ) |
自动(响应式系统) |
代码量 | 较多(需手动监听和渲染) | 较少(声明式模板) |
职责分离 | Model + View(Controller 混合在 View) | Model + View + ViewModel(清晰分层) |
适用场景 | 需要精细控制 DOM 的场景 | 快速开发数据驱动型应用 |
总结
通过MVC与MVVM比较,介绍了为什么MVC后会出现MVVM架构模式,主要是考虑开发效率,代码可维护和测试性考虑;分离关注点利用VM(视图模型),数据驱动视图,无需手动关注数据与视图双向绑定,日常编写Vue和React代码时,需要有意识的将VM(事件回调处理和数据请求等处理)与View(渲染内容)做隔离解耦,如下面:
javascript
const App = ()=>{
const [count,setCount] = useState(0)
/*VM*/
const handleClick = ()=>{
setCount(preCount=> preCount+=1)
}
/*View*/
return <div >
{{count}}
/* 不推荐,没有让VM 与 View 分离 */
<button onClick={()=>{
setCount(preCount=> preCount+=1) ❌
}}>点击</button>
<button onClick={handleClick}>点击</button> ✅
</div>
}
希望对你有帮助!如果有进一步问题,欢迎讨论 😊