概念
**MVVM (Model - View - ViewModel)** 最早由微软提出,它在 MVC 的基础之上,增加了数据绑定机制。
** 数据绑定(Data Binding)** 是一个实现 UI 中显示内容与数据对象(data object)相互关联的机制,它使得数据对象中的数据变化能够自动更新同步对应的 UI 显示内容中。反过来,也可以将用户在 UI 显示内容中进行的修改自动更新同步到数据对象(data object)中。
数据流方向
单向绑定(One-Way Data Binding)
当数据源更新时,这个变化会自动同步到目标对象中。而对于目标对象的变化,并不会被同步到数据源中。
目标对象可以为 UI 中的数据,则当数据源中的数据变化时,UI 界面上的对应数据就会自动更新。
双向绑定(Two-Way Data Binding) 除了数据源的更新可以自动同步到目标对象之外,当对于目标对象的变化,也会自动同步到数据源中。因此我们将其称之为双向绑定。
当目标对象为 UI 中的数据时,用户在界面上进行的修改就能自动更新到数据源中。
提供数据绑定的框架
- C#
- Windows Presentation Foundation
- JavaScript
- AngularJS
- Backbone.js
- React
- Vue.js
借助 MVVM 框架,可以帮助我们实现单向或双向的数据绑定。
下面,我们分别引用一个基于前端页面的 MVVM 框架(Vue.js)和基于客户端 - 服务器(Client-Server)的桌面级 MVVM 框架(Microsoft Windows Presentation Foundation),以帮助我们更好的理解数据绑定与 MVVM。
例子 1 - 基于前端页面的 MVVM 框架(Vue.js)
若在前端页面应用 MVVM,Model 可用纯 JavaScript 对象表示,View 负责数据在页面上的显示,ViewModel 则负责将 Model 中的数据同步到 View 显示出来,同时,当用户在 View 中修改了数据后,ViewModel 还需要将 View 中修改后的数据同步到 Model 中。
单向绑定(One-Way Data Binding)
JQuery 修改 DOM
我们先介绍一个 JQuery 实现的修改两个 DOM 元素的例子,以帮助我们更好地引入单向绑定。
我们有一个包含以下 HTML 的页面:
<p>Hello, <span id="name">Bart</span>!</p>
<p>You are <span id="age">12</span>.</p>
页面显示:
Hello, Bart!
You are 12.
用 JQuery 修改 name
和 age
节点中显示的内容:
var person = {
name: 'Homer',
age: 51
};
// JQuery code
$('#name').text(person.name);
$('#age').text(person.age);
当以上 JQuery 代码执行后,页面的内容应该发生了变化,变为:
Hello, Homer!
You are 15.
分析:
- 我们定义了
person
对象,以定义数据源(对应于 Model) - 通过 JQuery 代码实现将 Model 中的数据同步到页面(对应 View)中
应用前端 MVVM 框架
当我们应用了前端 MVVM 框架后(这里以 Vue.js 为例),我们只需要关注于 Model 中的数据,而 MVVM 框架本身会自动将 Model 中的数据变化映射到 DOM 中(对应上面的 JQuery 操作)。这样的自动映射使得前端开发者不再需要通过手动修改 DOM 而实现数据的显示更新。
从而,在处理包含用户输入交互较多的 “富表单型” 应用时,MVVM 框架本身可以为我们简化大量业务无关的代码。
基于 Vue.js 实现的单向绑定
<html>
<head>
<!-- 引用jQuery -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!-- 引用Vue -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.15/vue.js"></script>
<script>
// 初始化代码:
$(function () {
var vm = new Vue({
el: '#vm',
data: {
name: 'Robot',
age: 15
}
});
window.vm = vm;
});
</script>
</head>
<body>
<div id="vm">
<p>Hello, {{ name }}!</p>
<p>You are {{ age }} years old!</p>
</div>
</body>
<html>
在 VM 的核心代码中:
var vm = new Vue({
el: '#vm',
data: {
name: 'Robot',
age: 15
}
});
解析
el
属性指定了要把 Model 绑定到哪个 DOM 节点上,语法类似 JQuery(这里的#vm
对应了 ID 为 vm
的 DOM 节点,即 <div id="vm">...</div>
)。
data
属性指向了 Model,它是一个 JavaScript 对象。
在 <div id="vm">...</div>
中,使用 {{ name }}
可以直接引用 Model 中某个属性(这里对应于 data
对象中的 name
属性)。
操作
我们在浏览器中打开这个 HTML 页面,则会看到:
Hello, Robot!
You are 15 years old!
如果,在该页面上,使用 Chrome
并打开 Developer Tools
中的 Console
,执行:
window.vm.name = 'Bob'
在执行完成后,会观察到页面立刻发生了变化,原来的 Hello, Robot!
变成了 Hello, Bob!
。
事实上,我们在之前执行了 window.vm = vm
,即把 ViewModel 绑定到了 window
对象上。当我们修改 ViewModel 对应的 Model 时,MVVM 框架(这里对应 Vue.js)会自动监听 Model 的任何变化,并将变化后的 Model 数据更新到 View 的显示中。
这样只从 Model 到 View 的绑定称为单向绑定(One-Way Data Binding)。
双向绑定(Two-Way Data Binding)
在单向绑定中,仅把 Model 绑定到 View 上,即当 Model 中的数据更新时,View 中显示的数据也会自动被更新。
而如果当用户在 UI 中将 View 中的数据更新了,Model 也能自动更新。就称为双向绑定(Two-Way Data Binding)。
什么情况下,用户才能更新 View 中的数据呢?填写表单就是一个最直接的例子。
当用户填写表单(Form)时,View 中的数据就被更新了。如果 MVVM 框架能够将更新自动同步到对应的 Model 中,我们就称这里的 View 和 Model 做了双向绑定。
仍以 Vue.js 作为 MVVM 前端框架为例
<html>
<head>
<!-- 引用jQuery -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!-- 引用Vue -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.15/vue.js"></script>
<script>
// 初始化代码:
$(function () {
var vm = new Vue({
el: '#vm',
data: {
name: 'Robot',
email: 'a@b.com'
}
});
window.vm = vm;
});
</script>
</head>
<body>
<form id="vm" action="#">
<p><input v-model="name"></p>
<p><input v-model="email"></p>
</form>
</body>
<html>
解析:
- 我们添加了一个 HTML Form 表单
<form id="vm" action="#">...</form>
- 用
v-model
将某个<input>
标签与 Model 的某个属性作了双向绑定
操作:
- 在表单中输入内容(
a@example.com
和Tom
) - 通过执行
window.vm.name = "John"
,以在Console
中修改 Model 中name
的值 - 分别在每次操作前后,在
Chrome Developer Tools
的Console
中,执行window.vm.$data
以对比 Model 的变化
这说明,View 和 Model 中任何一方的变化都能自动被更新另一方,即 View 与 Model 作了双向绑定。
例子 2 - 桌面级 MVVM 框架(Microsoft Windows Presentation Foundation)
基本的数据绑定概念
… 待补充
Reference
- Wikipedia - Data binding
- 廖雪峰 - MVVM
- Microsoft - WPF Data binding Overview
- NativeScript - Data Binding