Vue2.x学习(二)Vue的MVVM模式

学习原文地址:浅探VUE的MVVM模式实现

1、MVVM模式思想

MVVM模式的思想:关注model(数据)的变化,让MVVM框架去自动更新DOM,实现方法主要是数据劫持结合发布订阅模式。


2、核心方法Object.defineProperty

Object.defineProperty是用来数据劫持的关键方法,vue框架是不兼容IE6~8低版本的,主要是因为它的用到了ES5中的这个Object.defineProperty的方法,而且这个方法暂时没有很好的降级方案。


该方法接收三个参数,而且都是必填的。

第一个参数:目标对象。

第二个参数:需要定义的属性或方法的名字。

第三个参数:目标属性所持有的特性。

value:属性的值。 writable:如果为 false ,属性的值就不能被重写,只能为只读了。 enumerable:是否可枚举,默认是false不可枚举的(通常设置为true) configurable:总开关,一旦为false,就不能再设置其他的(value,writable, enumerable) get():函数,获取属性值时执行的方法(不可以和writable、value属性共存) set():函数,设置属性值时执行的方法(不可以和writable、value属性共存)


通过Object.defineProperty这个方法,可以对引用数据实现监听,获取和修改值时分别调用get和set方法。

3、数据劫持


  1. 先定义初始化构造函数MyVue,通过它来获取我们传进来的数据data和定义的DOM节点范围,然后把data传进定义好的数据劫持方法observe。
  2. Observe实现了对数据的监听,多写一个observe的小方法用来new Observe,并且在里面做了引用数据类型的判断。这样做的目的是为了方便递归来实现数据结构的深层监听。
  3. 在设置新值时,也执行了一遍observe方法,是为了实现深度响应,因为在赋值的时候可能会赋值一个引用数据类型的值。
看看有没有实现数据劫持



可以看到对我们所定义的data中的数据都已经有了get和set方法了,到这里我们对data中数据的变化都是可以监听的到了。

4、数据代理

数据代理,我们用过vue的都知道,在实际使用中是能直接通过实例+属性(vm.name)直接获取到数据的,而我们上面的代码要获取到数据还需要这样myvue._data.name这样来获取到数据,中间多了一个 _data 的环节,这样使用起来不是很方便的。


下面我们来实现让我们的实例this来代理( _data)数据,从而实现 myvue.name 这样的操作可以直接获取到数据。


以上代码实现了数据代理,思想:就是在构建实例的时候,把data中的数据遍历一遍,依次加到this上,加的过程不要忘记添加Object.defineProperty,只要是数据我们都需要添加监听。

没有实现代理前:

可以通过vm._data访问



不能通过vm.直接访问



实现代理后



5、编译模板(Compile)

我们已经完成对数据的劫持,也实现了this对数据的代理,name接下来做的是怎么把数据编译到我们的dom节点上,也就是在View层展示我们的数据。


以上代码实现我们对数据的编译Compile如下图,可以看到我们把获取到el下面所有的子节点都存储到了文档碎片 fragment 中暂时存储了起来(放到内存中),因为这里要去频繁的操作DOM和查找DOM,所以移到内存中操作。

效果图:


  1. 先使用while循环,先把el中的所有子节点都添加到文档碎片中;
  2. 然后通过replace方法,遍历文档中的所有子节点,将他们的文本节点(node.nodeType = 3)带有{{}} 语法中的内容都获取到,把匹配到的值拆分成数组,然后遍历依次去data中查找获取,遍历的节点如果有子节点的话继续使用replace方法,直到反回undefined。
  3. 获取到数据后,用replace方法替换掉文本中{{}}的整块内容,然后在放回el元素中vm.$el.appendChild(fragment)。
6、关联视图(view)与数据(model)

如何实现通过修改数据引发视图的变化呢?涉及到js中的常用的js设计模式--发布订阅模式。

发布订阅模式的实现


这里用Dep方法来实现订阅和通知,在这个类中有addSub(添加)和notify(通知)两个方法,我们把将要做的事情(函数)通过addSub添加进数组里,等时机一到就notify通知里面所有的方法执行。

通过watcher创建的函数都会有一个update执行的方法可以方便我们调用。


把定义函数的方法watcher加到了replace方法里面,但是这里的watcher跟刚写编写的多了两个形参vm、RegExp.$1,而且写法也新增了一些内容,因为当new Watcher的时候会引发发生几个操作,来看完整代码:





1)首先看在Watcher构造函数中新增了一些私有属性分别代表:

  • Dep.target = this(在构造函数Dep.target临时存储着watcher的当前实例)
  • this.vm = vm(vm = myvue实例)
  • this.exp = exp(exp = 匹配的查找的对象'obj1.name'是字符串类型的值)
我们存储这些属性后,接下来就要去获取用exp匹配的字符串里面对于数据也就是 vm.a.b,但是此时的exp是个字符串,你不能直接这样取值vm[a.b]这是错误的语法,所以要循环去取到对应的值。


总而言之,就是在每个数据操作的部分,添加数据更新的功能,来实现数据的改变,使得view跟着改变。

效果图:


函数调用关系



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值