【Architectural Pattern】MVVM 与数据绑定

Posted by 西维蜀黍 on 2018-11-12, Last Modified on 2022-12-10

概念

**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修改nameage节点中显示的内容:

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.comTom
  • 通过执行window.vm.name = "John",以在Console中修改Model中name的值
  • 分别在每次操作前后,在Chrome Developer ToolsConsole中,执行window.vm.$data以对比Model的变化

这说明,View和Model中任何一方的变化都能自动被更新另一方,即View与Model作了双向绑定。

例子2 - 桌面级MVVM框架(Microsoft Windows Presentation Foundation)

基本的数据绑定概念

…待补充

Reference