0
点赞
收藏
分享

微信扫一扫

MVVM理解

首先要明确 MVVM 是什么,它是 MVC 的衍生架构。无论是 MVC 还是 MVVM 都不是只针对于前端或后端开发的,它们是针对于所有软件开发的架构。

MVC:

                     <------------- Model  <-------------

                     |更新                                        |控制

                    View                                     Constoller      

                     |                                                  |

                     <---sees--     User    --updates-->        

    Model(模型):是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。

    View(视图):是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的。

    Controller(控制器):是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。

    原理:1.View传送用户操作给Controller。

         2.Controller完成业务逻辑后,要求Model改变状态。

         3.Model将新数据发送到View,用户得到反馈。

    缺点:数据解析。将数据解析的部分放到了Controller里面,那么Controller就将变得相当臃肿。所以MVVM才诞生,将数据解析放到VM里去做。

        Controller的存在感被完全的降低了。MVVM中的持有关系就是:C持有VM,VM持有M。    

  MVC和MVVM的区别:

    1.数据解析。

    2.双向数据绑定。MVC是单向(View -> Model)  

 

MVVM:    

                                                              数据绑定

          View  <-------->  ViewModel       <---------      Model    

                                    ViewModel       --------->      Model  

                                                              事件监听  

M: 数据层Model,数据对象data:data: {msg: "world!"},data数据通过[数据绑定]给模板页面显示

V: 视图层View,模板页面:<div id="test"></div>,对DOM负责。通过 [事件监听]更新数据。

    [

     指令:自定义标签属性

     大括号表达式:显示数据

    ]

VM:视图模型层ViewModel,VUE实例,里面进行DOM事件监听和数据绑定(v-model),取名叫vm: const vm = new Vue({el: "选择器", data: {msg: "world!"}})

    将 数据层Model 和 视图层View 通过 [事件监听] 和 [数据绑定(v-model)] 连接起来。

[MVVM实现原理图]

                       

                        劫持监听data里所有属性--数据代理                         Dep与data中的属性一一对应

            |------->   Observer(监听器) --给每个属性创建Dep并通知变化--> Dep(消息订阅器,管理监听器和订阅者)         --通知变化-->

        new MVVM()                                                                    ^<--添加订阅者,把Watcher添加到Dep里的subs数组里--|                   |

            |------->   Compile(指令解析器)  --为表达式创建对应的Watcher,绑定更新函数-->                                  Watcher(订阅者)

                                 |--解析指令和{{}}表达式,[初始化]视图                通过绑定的更新函数[更新]视图--|      

                                             |-->   Updater(里面有对应的更新的方法)   <----|  

    初始化显示:通过[模板解析],页面(表达式和一般指令)能从data读取数据显示(编译和解析)。

    更新显示:通过[数据代理]和[数据绑定],更新data中的属性的数据,通过Object.defineProperty()里的set(data里的set)函数更新界面。

    [初始化完整流程]  

        1. 先new一个MVVM实例,内部先进行[数据代理],通过方法Object.defineProperty()[给vm添加与data对象的属性对应的属性描述符],get方

            法实现代理读操作,set方法实现代理写操作。

        2. 创建【Observer监听器】,通过方法Object.defineProperty()里的set方法来[劫持监听]data里的所有属性实现[数据绑定]。

           同时创建给每个属性创建【Dep消息订阅器】,[Dep对象与data中的属性一一对应]。

           同时创建【Compile指令解析器】,

              解析器一方面调用Updater方法解析指令和双大括号表达式,来初始化视图[模板解析];

              解析器另一方面会创建[和表达式对应的【Watcher订阅者】],在Watcher里指定了更新数据的回调函数,并且把Watcher添加到Dep对象里的用来

              保存订阅者的subs数组里,用来监听Dep对象里属性的变化,如果属性改变,就调用这些回调函数更新数据到界面,就这样建立了Dep与Watcher之

              间的关系,是在监听器里的get方法完成的,此get方法的另一个作用是获得当前属性值。

    [更新显示完整流程]

        1. this.name='Lucy';修改了vm里面的属性值,被vm里的set函数监视到,vm里的set函数就会去修改data里的name属性值[数据代理],又被data里

            的set函数监视到[数据绑定],然后data里的set函数去更新界面。

        2. set怎么更新界面?data里的set是Observer的原型对象里的Object.defineProperty()里的set,set函数通过Observer监听到数据改变,通知

            Dep消息管理器(对应data中的属性),在Dep对象里通知所有相关的Watcher订阅者(对应非事件指令),Watcher对象会先取到最新的属性值,然后

            去调用更新的回调函数(赋值操作)实现了更新数据。再修改其他的属性值,从set开始循环即可。

        [举例:vm.name = 'abc'-->data中的name属性值变化 -->name的set()调用 -->Dep -->相关的所有的Watcher -->cb() -->Updater更新]

    创建这些对象有先后顺序:

        首先创建Observer构造函数,

        然后创建Observe功能函数,通过new一个Observer实例,对data中所有层次的属性通过数据劫持实现数据绑定。

        然后创建Dep构造函数,在Observer的原型对象里new一个Dep实例,

        然后创建Compile构造函数,在MVVM(Vue)里new一个Compile实例来解析模板(解析大括号表达式和一般指令)。

        然后创建Watcher构造函数,在Compile初始化编译模板时创建Watcher,并在Watcher的原型对象里建立Dep和Watcher之间的关系。

            什么时候建立?初始化的解析模板中的表达式创建Watcher对象时。在watcher的原型对象里建立Dep和Watcher的关系:

                addDep: function (dep) {

                    if (!this.depIds.hasOwnProperty(dep.id)) {  // 判断如果已存在关系,则不再建立

                      // [建立dep到watcher,把watcher添加到dep里的subs数组里。]this是watcher。【这个关系是关键】

                      dep.addSub(this);          

                      // [建立watcher到dep的关系,把dep添加到watcher里的depIds对象里。]this是watcher。【这个关系只是为了判断是否已存在关系】

                      this.depIds[dep.id] = dep;

                    }

                },

           1. 什么时候一个dep中关联多个watcher?

              多个指令或表达式用到了当前同一个属性  {{name}} {{name}}

           2. 什么时候一个watcher中关联多个dep?

               多层表达式的watcher对应多个dep    {{a.b.c}}    

        最后创建MVVM(Vue)构造函数。在MVVM(Vue)的原型对象里实现数据代理。

v-model指令是怎么实现双向数据绑定的?

    [1] 双向数据绑定是建立在单向数据绑定(数据层绑定视图层)[model=>view]的基础之上的。

      [初始化完整流程]  

          (1) 先new一个MVVM实例,内部先进行[数据代理],通过方法Object.defineProperty()[给vm添加与data对象的属性对应的属性描述符],get方

              法实现代理读操作,set方法实现代理写操作。

          (2) 创建Observer监听器,通过方法Object.defineProperty()里的set方法来劫持监听data里的所有属性实现[数据绑定]。

             同时创建Dep消息订阅器,[Dep对象与data中的属性一一对应]。

             同时创建Compile指令解析器,

                解析器一方面调用Updater方法解析指令和双大括号表达式,来初始化视图[模板解析];

                解析器另一方面会创建[和表达式对应的Watcher]订阅者,在Watcher里指定了更新数据的回调函数,并且把Watcher添加到Dep对象里的用来保存订

                  阅者的subs数组里,用来监听Dep对象里属性的变化。如果属性改变,就调用这些回调函数更新数据到界面,就这样建立了Dep与Watcher之间的关

                  系,是在监听器里的get方法完成的,此get方法的另一个作用是获得当前属性值。

      [更新显示完整流程]

          (1) this.name='Lucy';修改了vm里面的属性值,被vm里的set函数监视到,vm里的set函数就会去修改data里的name属性值[数据代理],又被data里

              的set函数监视到[数据绑定],然后data里的set函数去更新界面。

          (2) set怎么更新界面?data里的set是Observer的原型对象里的Object.defineProperty()里的set,set函数通过Observer监听到数据改变,

              通知Dep消息管理器(对应data中的属性),在Dep对象里通知所有相关的Watcher订阅者(对应非事件指令),Watcher对象会先取到最新的属性值,

              然后去调用更新的回调函数(赋值操作)实现了更新数据。再修改其他的属性值,从set开始循环即可。

          [举例:vm.name = 'abc'-->data中的name属性值变化 -->name的set()调用 -->Dep -->相关的所有的Watcher -->cb() -->Updater更新]  

    [2] 视图层绑定数据层的实现流程:通过v-model指令实现[view=>model]  

      [初始化完整流程] 在解析v-model指令时,给当前元素添加input监听。【input输入内容的过程中触发,change是失去焦点才触发】

          (1) 解析v-model时,在Compile原型对象里先执行compile方法里解析普通指令的语句:

                compileUtil[dir] && compileUtil[dir](node, me.$vm, exp);

            然后会进入到[指令处理集合compileUtil对象]里找到[解析v-model的方法model],该方法里调用[真正用于解析指令的方法bind],在bind里根据

              指令名(model)进入到对应的更新节点函数updater里modelUpdater方法,实现初始化,同时,bind里会给表达式model创建一个Watcher。

              modelUpdater: function (node, value, oldValue) {  // node是input当前元素,value是msg的初始值"haha",oldValue是undefined

                node.value = typeof value == 'undefined' ? '' : value; // 把初始值"haha"赋值给input输入框,初始显示

              }  

          (2) 然后执行方法model里bind后面的代码,获取表达式model的值msg,添加input事件监听(方法addEventListener),用于处理视图层变

            化更新数据层数据。  

      概括:解析v-model时,进入解析指令的方法bind里,一方面[根据指令名model初始化当前元素],另一个方面[给表达式model创建一个Watcher。]

           执行完bind之后,会获取表达式model的值msg,然后[添加input事件监听(方法addEventListener)],用于处理视图层变化更新数据层数据。

      [更新显示完整流程] 当input的value发生改变时,将最新的值赋值给当前表达式model所对应的data属性msg。          

          (1) [手动改变输入框的值,即value发生变化,input事件监听到后调用model函数(v-model绑定的函数)的set方法将最新的值赋值给当前表达式所

            对应的数据层data]里的msg属性,

          (2) 此时,[data里的set函数监视到]属性msg改变了,再走一遍[数据绑定]的流程,实现把更新后的内容显示到界面上。

举报

相关推荐

0 条评论