MVVM和vue.js(简单实例,数据链)

MVVM简介

Vue.js简介

Vue与Angular 1

vue实例

一个简单实例从创建到销毁的完整生命周期

数据响应式原理和数据链

数据绑定视图

MVVM简介

模型—视图—视图模型(Model-View-ViewModel, MVVM),本质上是MVC(模型—视图—控制器)的改进版,其最重要的特性即是数据绑定(data binding),此外还包括依赖注入、路由配置、数据模板等一些特性。
从MVC到MVVM 模型—视图—控制器(Model-View-Controller, MVC)模式,在Web 1.0时代曾被广泛应用于Web架构中,然而其诞生的时间却比Web早几年。最初,MVC被应用于桌面程序中,在PHP、JSP等脚本语言诞生之后,也逐渐成为Web开发的主流模式。
View视图层是用户能够看到并进行交互的客户端界面,如桌面应用的图形界面、浏览器端渲染的网页等;Model指业务模型,用于计算、校验、处理和提供数据,但不直接与用户产生交互;Controller控制器则负责收集用户输入的数据,向相关模型请求数据并返回相应的视图来完成交互请求.
MVC模式实现了M和V的代码分离,M专注于数据,V专注于表达,C则在M和V之间架起了一座桥梁。即使采用同一个Model的数据,如果调用不同的View(如柱状图和表格),也会得到不同的页面呈现。这样的设计,不仅减少了Model层的冗余代码,使得Model和View更加灵活和易于维护,同时也简化了项目的架构和管理。
MVC渐渐演化出更多形态,有的开发者也会将其统一称作“MV*模式”,MVVM即是其中的一种。 与MVC模式一样,MVVM的主要目的是分离视图(View)和模型(Model),ViewModel层封装了界面展示和操作的属性和接口。通过数据绑定,我们可以将View和ViewModel关联在一起,当ViewModel中的数据发生变化时,View也会同步进行更新.

MVVM模式解耦了视图和模型。在模式中,每一个视图都有对应的一个ViewModel,同时ViewModel与模型建立联系。当接收到用户请求后,ViewModel获取模型响应的数据,并通过数据绑定将相应的视图页面重新渲染。模型层的数据只需要传入ViewModel即可实现视图的同步更新,从而实现了视图和模型之间的松散耦合。 与MVC不同的是,**MVC是系统架构级别的,而MVVM是用于单页面上的。**因此,MVVM的灵活性要远大于MVC。如果将这里的M抛开,只看VVM的话,那这就是一个组件(如treeview)的设计模式。所以,MVVM模式也是组件化开发的最佳实践。

Vue.js简介

Vue.js是一套轻量级MVVM框架。 与其他重量级框架不同的是**,Vue的核心库只关注视图层**,并且提供尽可能简单的API以实现数据绑定、组件复用等机制,且非常容易学习并混入其他库。同时,Vue也完全有能力支持采用SPA设计和组合其他Vue生态库的系统。

React和Vue的速度都很快,不过显然Vue的渲染速度要更快一些,这是因为React中有大量用于提供警告和错误提示信息的检查机制。React和Vue都将着重点放在核心库上,也都有专门负责路由和全局状态管理等功能的配套库。例如,与React配套的有React Router、Redux,与Vue配套的有Vue Router、Vuex。
React采用JSX渲染组件,而Vue则采用模板,比如.vue后缀的文件。 JSX是使用XML语法编写Javascript的一种语法糖。语法如下:

  class HelloMessage extends React.Component {
       render() {
                return (
            <div>
              Hello {this.props.name}
            </div>
         );
       }}
 ReactDOM.render(
   <HelloMessage name="Taylor" />,
   mountNode
 );

Vue的许多语法和Angular十分相似,可以认为Angular是Vue的灵感之源。
Angular 2语法:

  <input type="text" [(ngModel)]="name"/>
     <button (click)="onSave($event)">Save</button>
     <ul>
       <li *ngFor="letheroofheroes" [title]="hero.name" (click)="delete
     (hero)">{
   
   {hero.name}}</li>
     </ul>
     <form #heroForm (ngSubmit)="submit()"></form>

Vue语法:

 <input type="text" v-model="name"/>
     <button v-on:click="on Save($event)">Save</button>
     <ul>
        <li v-for="heroinheroes" v-bind:title="hero.name" v-on:click="delete
     (hero)">{
   
   {hero.name}}</li>
     </ul>
     <form ref="heroForm" v-on:submit="submit()"></form>

**MVVM模式解耦了视图和模型。**在模式中,每一个视图都有对应的一个ViewModel,同时ViewModel与模型建立联系。当接收到用户请求后,ViewModel获取模型响应的数据,并通过数据绑定将相应的视图页面重新

Vue与Angular 1

Vue与Angular 1相比最大的区别在于没有脏检测机制。在Angular 1中存在多个watcher,当watcher越来越多时,检测耗时会越来越长。因为作用域中每发生一次变化,所有watcher都要重新计算,而一些watcher在计算之后可能又会导致新的变化,并引发所有watcher重新计算,从而进入一种无限循环的脏检测。 Angular 1的处理方式是设置循环上限,比如10次,当循环达到10次,即中止循环。显然,这种脏检测机制性能十分低下、耗时长,并不适合大型Web应用。 Vue的处理方式则是全局只设置一个watcher,用这一个watcher来记录和更新一组关联对象的值,从而回避了脏检测的问题。 有意思的是,Vue最初是参考Angular的,而Angular 2则借鉴了Vue的机制,采用相似的设计来解决脏检测存在的问题。

轻、重量级框架划分的标准是,是否过分参与系统结构级的架构和功能上的伸缩拓展。和Vue、React这样的轻量级框架相比,Angular在单向数据流的视图渲染、事件绑定之外,还参与了View对Model层的数据更新,即双向数据绑定。显然,它是一个重量级框架。

vue实例

一个简单实例从创建到销毁的完整生命周期

![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121165444343.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMzU4NTc0,size_16,color_FFFFFF,t_70在这里插入图片描述

在这个实例中,初始化了带有title数据的vm对象,并将其绑定到id为app的DOM节点上。

<body>
<div id="app">
    <h1>{
   
   { title }}</h1>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js">

    </script>
<script type="text/javascript">
    var vm = new Vue({
     
     el: '#app', // 绑定(mount)到DOM上
        data () {
     
     
            return {
     
     
                title: 'Hello World'
            }
        }
    })
</script>
</body>

将上一简单实例稍微改造一下,为其绑定钩子函数并打印标识信息,用以观察这些钩子函数执行的时机:
(console.log() 方法用于在控制台输出信息)

<body>
<div id="app">
    <h1>{
   
   { title }}</h1>
    <button @click="randomTitle()">改变title</button>
    <button @click="destoryVm()">销毁实例</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js">

</script>
<script type="text/javascript">
    var vm = new Vue({
     
     
            el: '#app',   // mount到DOM上
            data () {
     
     
                return {
     
     
                    title: 'Hello World'
                }
            },
            methods: {
     
     
                randomTitle () {
     
     
                    this.title = 'Hello ' + ['China', 'World', 'Universe'][Math.
                    floor(Math.random() * 2.999)]
                },
                destoryVm () {
     
     
                    this.$destroy()
                }
            },
        // 实例初始化之后,数据观测和事件绑定之前
            beforeCreate () {
     
     
                console.log('before create')
            },
            // 实例初始化完成,挂载尚未开始时
            created () {
     
     
                console.log('created')
            },
            // 挂载之前,render函数首次被调用时
            beforeMount () {
     
     
                console.log('before mount')
            },
            // 在实例挂载到DOM节点上之后
            mounted () {
     
     
                console.log('mounted')
            },
        // 数据更新时,在虚拟DOM状态变化之前
        beforeUpdate () {
     
     
            console.log('before update')
        },
        // 虚拟DOM被重新渲染之后
        updated () {
     
     
            console.log('updated')
        },
        // 实例销毁之前,此时实例依然可用
        beforeDestroy () {
     
     
            console.log('before destroy')
        },
        // 实例销毁后,此时Vue实例及其子实例将完全解绑
        destroyed () {
     
     
            console.log('destroyed')
        }
    })
</script>
</body>

效果:

在这里插入图片描述
按下改变title:
在这里插入图片描述
开发人员工具:
在这里插入图片描述

按下销毁之后再按改变title无反应。
在这里插入图片描述

数据响应式原理和数据链

Vue中最重要的概念就是响应式数据,一方面指衍生数据和元数据之间的响应,通过数据链来实现;另一方面则是指视图与数据之间的绑定.
初识数据链
数据链在学术上被定义为连通数据的链路。在这条链路上有一到多个数据起点(元数据),并通过该点不断衍生拓展新的节点(衍生数据),形成一个庞大的网状结构。当你修改数据起点时,所有存在在网上的节点值都将同步更新,但是衍生数据应该怎样实现从而保证其值只依赖于元数据而不允许被外界修改呢?这里先介绍一下函数式编程的概念。 函数式编程(Functional Programming)是一种结构化编程方式,力求将运算过程写成一系列嵌套的函数调用。 源于JS中“万物皆对象”的理念,函数式编程认定函数是第一等公民,可以赋值给其他变量、用作另一个函数的参数或者作为函数返回值来使用。 由于其作用是处理运算,因此函数体只能包含运算过程,而且必带返回值(在实际开发中,不做I/O读写操作是不可能的,不过要把I/O限制到最小),标准格式如下:
let double = function (num) {
return num * 2
}
Vue中的数据链 Vue实例提供了computed计算属性选项,以供开发者生成衍生数据对象。虽然计算属性以函数形式声明,却并不接受参数,也只能以属性的方式调用。由于计算属性的this指向Vue实例,所以它可以获取实例上所有已挂载的可见属性。

实例:

<body>
<style>
    #app {
     
     
        font-family: Roboto, sans-serif;
        color: #363e4f;
    }
    .data-label {
     
     
        display: inline-block;
        width: 160px;
    }
</style>
<div id="app">
    <p><strong class="data-label">A</strong><input type="text" v-model="a"></p>
    <p><strong class="data-label">B</strong><input type="text" v-model="b"></p>
    <p><strong class="data-label">C=A*2+2</strong>{
   
   {c}}</p>

    <p><strong class="data-label">D=A+B*2</strong>{
   
   { d }}</p >
    <p><strong class="data-label">E=B/2</strong>{
   
   { e }}</p >
    <p><strong class="data-label">F=C+D</strong>{
   
   { f }}</p >
    <p><strong class="data-label">G=D-E</strong>{
   
   { g }}</p >
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js">

</script>
<script type="text/javascript">
    let vm = new Vue({
     
     
         el: '#app',
         data () {
     
     
            return {
     
     
              a: 3,
              b: 4
       }
     },
     computed: {
     
       // 计算属性
       c () {
     
     
         return this.a * 2 + 2
       },
       d () {
     
     
         return Number(this.a) + this.b * 2
       },
       e () {
     
     
       return this.b / 2
       },
            f () {
     
     
              return Number(this.c) + Number(this.d)
            },
            g () {
     
     
              return this.d - this.e
            }
         }
       })
     </script>
</body>

在这里插入图片描述
在这里插入图片描述

数据绑定视图

这是一个含有字符串类型属性profile的对象: let obj = {
profile: ‘’
}
不过,身为对象属性的profile仅仅只是个字符串吗?在控制台中使用Object API中的getOwnPropertyDescriptor方法将其“内在”打印出来:

<body>
<span id="harry" style="line-height: 32px;">&nbsp;</span><br>
<input id="trigger" type="text">
<script type="text/javascript">
    let harry = document.getElementById('harry')
    let trigger = document.getElementById('trigger')
    let key = 'profile'  // 对象属性键名
    let store = {
     
     }      // 辅助get取值
    let obj = {
     
              // 对象
        profile: ''
    }
    Object.defineProperty(obj, key, {
     
     
        set (value) {
     
     
            harry.innerText = value  // 重点:修改DOM节点视图
            store[key] = value
        },
        get () {
     
     
            return store[key]
        }
    })
    trigger.addEventListener('keyup', function () {
     
     
        obj[key] = this.value
        console.log(obj[key])
    })
</script>
</body>

在这里插入图片描述

上述代码中,在对象属性的setter函数中修改文本节点的值,所以当obj.profile被重新赋值时,节点视图也会同步更新;然后对输入框添加事件监听(addEventListener),当用户事件触发时,输入值将被赋于obj.profile。以此方式,我们实现了数据与视图之间的“双向绑定”,这也是Vue数据与视图绑定的实现原理。

在Vue中,当我们把普通的JavaScript对象传给Vue实例的data选项时,Vue将遍历对象属性,并使用Object.defineProperty将其全部转化为getter/setter,并在组件渲染时将属性记录为依赖。之后当依赖项的setter函数被调用时,会通知watcher重新计算并更新其关联的所有组件

来源:《vue.js从入门到实战》

猜你喜欢

转载自blog.csdn.net/qq_41358574/article/details/112860352
今日推荐