概念
**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