VUE自学日志01-MVC和MVVM

一.需要了解的基础概念

  • Model(M)是指数据模型,泛指后端进行的各种业务逻辑处理和数据操控,主要围绕数据库系统展开。这里的难点主要在于需要和前端约定统一的接口规则。
  • View(V)是视图层,也就是用户界面。前端主要由HTML和CSS来构成,为了更方便地展现ViewModel或者Model层的数据。
  • ViewModel(VM)由前端开发人员组织生成和维护的视图数据层。在这一层,前端开发者从后端获取得到Model数据进行转换出来,做二次封装,以生成符合View层使用预期的视图数据模型。视图状态和行为都封装在ViewModel里。这样的封装使得ViewModel可以完整地去描述View层。

二.什么是MVVM

MVVM(Model-View-ViewModel)是一种软件架构设计模式,它是一种简化用户界面的事件驱动编程方式。
image
在MVVM架构中,是不允许数据和视图直接通信的,只能通过ViewModel来通信,而ViewModel就是定义了一个Observer观察者。ViewModel是连接View和Model的中间件。

  • ViewModel能够监听到视图的变化,并能够通知数据发生变化。
  • ViewModel能够观察到数据的变化,并对视图对应的内容进行更新。
    image

Vue.js就是一个MVVM的实现者,它的核心就是实现了DOM监听与数据绑定。

三.为什么要使用MVVM

  • 低耦合:视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,当View变化的时候Model可以不变,当Model变化的时候,View也可以不变。
  • 高复用:可以把一些视图逻辑放到一个ViewModel里面,让很多View重用这段视图逻辑。
  • 前后端分离:开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
  • 可测试:界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。

Vue.js 是什么

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

如果你想在深入学习 Vue 之前对它有更多了解,我们制作了一个视频,带您了解其核心概念和一个示例工程。

如果你已经是有经验的前端开发者,想知道 Vue 与其它库/框架有哪些区别,请查看对比其它框架

起步

(CDN引入)尝试 Vue.js 最简单的方法是使用 Hello World 例子。你可以在浏览器新标签页中打开它,跟着例子学习一些基础用法。或者你也可以创建一个 .html 文件,然后通过如下方式引入 Vue:

<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="//cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

或者:

<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="//cdn.jsdelivr.net/npm/vue@2"></script>

安装教程给出了更多安装 Vue 的方式。请注意我们不推荐新手直接使用 vue-cli,尤其是在你还不熟悉基于 Node.js 的构建工具时。

如果你喜欢交互式的东西,你也可以查阅这个 Scrimba 上的系列教程,它揉合了录屏和代码试验田,并允许你随时暂停和播放。

声明式渲染

Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="//cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
    <div id="app">
        {{ message }}
    </div>
</body>
<script>
    var app = new Vue({
        el: '#app',
        data: {
            message: 'Hello Vue!'
        }
    })
</script>
</html>

看起来这跟渲染一个字符串模板非常类似,但是 Vue 在背后做了大量工作。现在数据和 DOM 已经被建立了关联,所有东西都是响应式的。我们要怎么确认呢?打开你的浏览器的 JavaScript 控制台 (就在这个页面打开),并修改 app.message 的值,你将看到上例相应地更新。

注意我们不再和 HTML 直接交互了。一个 Vue 应用会将其挂载到一个 DOM 元素上 (对于这个例子是 #app) 然后对其进行完全控制。那个 HTML 是我们的入口,但其余都会发生在新创建的 Vue 实例内部。

除了文本插值,我们还可以像这样来绑定元素 attribute:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="//cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>

<div id="app-2">
    <span v-bind:title="message">
    鼠标悬停几秒钟查看此处动态绑定的提示信息!
    </span>
</div>

</body>
<script>
    var app2 = new Vue({
        el: '#app-2',
        data: {
            message: '页面加载于 ' + new Date().toLocaleString()
        }
    })
</script>
</html>

条件与循环

控制切换一个元素是否显示也相当简单:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="//cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>

<div id="app-3">
    <p v-if="seen">现在你看到我了</p>
</div>

</body>
<script>
    var app3 = new Vue({
        el: '#app-3',
        data: {
            seen: true
        }
    })
</script>
</html>

继续在控制台输入 app3.seen = false,你会发现之前显示的消息消失了。

这个例子演示了我们不仅可以把数据绑定到 DOM 文本或 attribute,还可以绑定到 DOM 结构。此外,Vue 也提供一个强大的过渡效果系统,可以在 Vue 插入/更新/移除元素时自动应用过渡效果。

还有其它很多指令,每个都有特殊的功能。例如,v-for 指令可以绑定数组的数据来渲染一个项目列表:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="//cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>

<div id="app-4">
    <ol>
        <li v-for="todo in todos">
            {{ todo.text }}
        </li>
    </ol>
</div>

</body>
<script>
    var app4 = new Vue({
        el: '#app-4',
        data: {
            todos: [
                { text: '学习 JavaScript' },
                { text: '学习 Vue' },
                { text: '整个牛项目' }
            ]
        }
    })
</script>
</html>

在控制台里,输入 app4.todos.push({ text: ‘新项目’ }),你会发现列表最后添加了一个新项目。

处理用户输入

为了让用户和你的应用进行交互,我们可以用 v-on 指令添加一个事件监听器,通过它调用在 Vue 实例中定义的方法:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="//cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>

<div id="app-5">
    <p>{{ message }}</p>
    <button v-on:click="reverseMessage">反转消息</button>
</div>

</body>
<script>
    var app5 = new Vue({
        el: '#app-5',
        data: {
            message: 'Hello Vue.js!'
        },
        methods: {
            reverseMessage: function () {
                this.message = this.message.split('').reverse().join('')
            }
        }
    })
</script>
</html>

注意在 reverseMessage 方法中,我们更新了应用的状态,但没有触碰 DOM——所有的 DOM 操作都由 Vue 来处理,你编写的代码只需要关注逻辑层面即可。

Vue 还提供了 v-model 指令,它能轻松实现表单输入和应用状态之间的双向绑定。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="//cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>

<div id="app-6">
    <p>{{ message }}</p>
    <input v-model="message">
</div>

</body>
<script>
    var app6 = new Vue({
        el: '#app-6',
        data: {
            message: 'Hello Vue!'
        }
    })
</script>
</html>

组件化应用构建

组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。仔细想想,几乎任意类型的应用界面都可以抽象为一个组件树:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="//unpkg.com/vue@next"></script>
</head>
<body>
    <div id="components-app" class="demo">
        <ol>
            <!--
              Now we provide each todo-item with the todo object
              it's representing, so that its content can be dynamic.
              We also need to provide each component with a "key",
              which will be explained later.
            -->
            <todo-item v-for="item in groceryList" v-bind:todo="item" v-bind:key="item.id"></todo-item>
        </ol>
    </div>
</body>
<script>
    const ComponentsApp = {
        data() {
            return {
                groceryList: [
                    { id: 0, text: 'Vegetables' },
                    { id: 1, text: 'Cheese' },
                    { id: 2, text: 'Whatever else humans are supposed to eat' }
                ]
            }
        }
    }

    const app = Vue.createApp(ComponentsApp)

    app.component('todo-item', {
        props: ['todo'],
        template: `<li>{{ todo.text }}</li>`
    })

    app.mount('#components-app')
</script>
</html>

尽管这只是一个刻意设计的例子,但是我们已经设法将应用分割成了两个更小的单元。子单元通过 prop 接口与父单元进行了良好的解耦。我们现在可以进一步改进 组件,提供更为复杂的模板和逻辑,而不会影响到父应用。

与自定义元素的关系

你可能已经注意到 Vue 组件非常类似于自定义元素——它是 Web 组件规范的一部分,这是因为 Vue 的组件语法部分参考了该规范。例如 Vue 组件实现了 Slot API 与 is attribute。但是,还是有几个关键差别:

  1. Web Components 规范已经完成并通过,但未被所有浏览器原生实现。目前 Safari 10.1+、Chrome 54+ 和 Firefox 63+ 原生支持 Web Components。相比之下,Vue 组件不需要任何 polyfill,并且在所有支持的浏览器之下表现一致 (IE 11 除外——请移步阅读这里的详情)。必要时,Vue 组件也可以包裹于原生自定义元素之内。
  2. Vue 组件提供了纯自定义元素所不具备的一些重要功能,最突出的是跨组件数据流、自定义事件通信以及构建工具集成。

虽然 Vue 内部没有使用自定义元素,不过在应用使用自定义元素、或以自定义元素形式发布时,依然有很好的互操作性。Vue CLI 也支持将 Vue 组件构建成为原生的自定义元素。

Tags: