Vue数据双向绑定原理是通过数据劫持结合发布者-订阅者模式的方式来实现的,首先是对数据进行监听,然后当监听的属性发生变化时则告诉订阅者是否要更新,若更新就会执行对应的更新函数从而更新视图
MVC模式 以往的MVC模式是单向绑定,即Model绑定到View,当我们用JavaScript代码更新Model时,View就会自动更新
MVVM模式 MVVM模式就是Model–View–ViewModel模式。它实现了View的变动,自动反映在 ViewModel,反之亦然。对于双向绑定的理解,就是用户更新了View,Model的数据也自动被更新了,这种情况就是双向绑定。再说细点,就是在单向绑定的基础上给可输入元素input、textare等添加了change(input)事件,(change事件触发,View的状态就被更新了)来动态修改model。
双向绑定原理 vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的 我们已经知道实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令(如v-model,v-on)对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。 因此接下去我们执行以下3个步骤,实现数据的双向绑定: (1)实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。 (2)实现一个订阅者Watcher,每一个Watcher都绑定一个更新函数,watcher可以收到属性的变化通知并执行相应的函数,从而更新视图。 (3)实现一个解析器Compile,可以扫描和解析每个节点的相关指令(v-model,v-on等指令),如果节点存在v-model,v-on等指令,则解析器Compile初始化这类节点的模板数据,使之可以显示在视图上,然后初始化相应的订阅者(Watcher)。
实现一个Observer
Observer是一个数据监听器,其实现核心方法就是Object.defineProperty( )。如果要对所有属性都进行监听的话,那么可以通过递归方法遍历所有属性值,并对其进行Object.defineProperty( )处理
在Observer中,当初我看别人的源码时,我有一点不理解的地方就是 实现一个Watcher Watcher就是一个订阅者。用于将Observer发来的update消息处理,执行Watcher绑定的更新函数。 如下代码实现了一个Watcher
在我研究代码的过程中,我觉得最复杂的就是理解这些函数的参数,后来在我输出了这些参数之后,函数的这些功能也容易理解了。vm,就是之后要写的SelfValue对象,相当于Vue中的new Vue的一个对象。exp是node节点的v-model或
上面的代码中就可以看出来,在Watcher的getter函数中, var value = this.vm.data[this.exp] // 强制执行监听器里的get函数。 这里获取vm.data[this.exp] 时,会调用Observer中Object.defineProperty中的get函数 get: function getter () { if (Dep.target) { // 在这里添加一个订阅者 console.log(Dep.target) dep.addSub(Dep.target); } return val; },
从而把watcher添加到了订阅器中,也就解决了上面 实现一个Compile
Compile主要的作用是把new SelfVue 绑定的dom节点,(也就是el标签绑定的id)遍历该节点的所有子节点,找出其中所有的v-指令和" {{}} ".
(2)如果子节点是文本节点,即" {{ data }} ",则用正则表达式取出" {{ data }} "中的data,然后 实现一个MVVM 可以说MVVM是Observer,Compile以及Watcher的“boss”了,他需要安排给Observer,Compile以及Watche做的事情如下
(1)Observer实现对MVVM自身model数据劫持,监听数据的属性变更,并在变动时进行notify 最后写一个html测试一下我们的功能 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>self-vue</title></head><style> #app { text-align: center; }</style><body> <div id="app"> <h2>{{title}}</h2> <input v-model="name"> <h1>{{name}}</h1> <button v-on:click="clickMe">click me!</button> </div></body><script src="js/observer.js"></script> <script src="js/watcher.js"></script> <script src="js/compile.js"></script> <script src="js/mvvm.js"></script> <script type="text/javascript"> var app = new SelfVue({ el: '#app', data: { title: 'hello world', name: 'canfoo' }, methods: { clickMe: function () { this.title = 'hello world'; } }, mounted: function () { window.setTimeout(() => { this.title = '你好'; }, 1000); } });</script></html> 先执行mvvm中的new SelfVue(...),在mvvm.js中, observe(this.data); new Compile(options.el, this);
先初始化一个监听器Observer,用于监听该对象data属性的值。 <input v-model="name"> 即输入框的值发生变化,就会触发Compile中的 node.addEventListener('input', function(e) { var newValue = e.target.value; if (val === newValue) { return; } self.vm[exp] = newValue; val = newValue; });
(责任编辑:yang) |