以往的MVC模式是单向绑定,即Model绑定到View,当我们⽤JavaScript代码更新Model时,View就会⾃动更新
vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的⽅式来实现的。 我们已经知道实现数据的双向绑定,⾸先要对数据进⾏劫持监
1.实现⼀个Observer
Observer是⼀个数据,其实现核⼼⽅法就是Object.defineProperty( )。如果要对所有属性都进⾏监听的话,那么可以通过递归⽅法遍历所有属性值,并对其进⾏Object.defineProperty( )处理 如下代码实现了⼀个Observer。
function Observer(data) {
this.data = data;
this.walk(data);
}
Observer.prototype = {
walk: function(data) {
var self = this;
//这⾥是通过对⼀个对象进⾏遍历,对这个对象的所有属性都进⾏监听
Object.keys(data).forEach(function(key) {
self.defineReactive(data, key, data[key]);
});
},
defineReactive: function(data, key, val) {
var dep = new Dep();
// 递归遍历所有⼦属性
var childObj = observe(val);
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function getter () {
if (Dep.target) {
// 在这⾥添加⼀个订阅者
console.log(Dep.target)
dep.addSub(Dep.target);
}
return val;
htmlradio添加切换事件},
/
/ setter,如果对⼀个对象属性值改变,就会触发setter中的ify(),通知watcher(订阅者)数据变更,执⾏对应订阅者的更新函数,来更新视图。            set: function setter (newVal) {
if (newVal === val) {
return;
}
val = newVal;
// 新的值是object的话,进⾏监听
childObj = observe(newVal);
}
});
}
};
function observe(value, vm) {
if (!value || typeof value !== 'object') {
return;
}
return new Observer(value);
};
// 消息订阅器Dep,订阅器Dep主要负责收集订阅者,然后在属性变化的时候执⾏对应订阅者的更新函数
function Dep () {
this.subs = [];
}
Dep.prototype = {
/**
* [订阅器添加订阅者]
* @param  {[Watcher]} sub [订阅者]
*/
addSub: function(sub) {
this.subs.push(sub);
this.subs.push(sub);
},
// 通知订阅者数据变更
notify: function() {
this.subs.forEach(function(sub) {
sub.update();
});
}
};
Dep.target = null;
在Observer中,当初我看别⼈的源码时,我有⼀点不理解的地⽅就是Dep.target是从哪⾥来的,相信有些⼈和我会有同样的疑问。这⾥不着急,当写到Watcher的时候,你就会发现,这个Dep.target是来源于Watcher。
2.实现⼀个Watcher
Watcher就是⼀个订阅者。⽤于将Observer发来的update消息处理,执⾏Watcher绑定的更新函数。 如下代码实现了⼀个Watcher
function Watcher(vm, exp, cb) {
this.cb = cb;
this.vm = vm;

this.value = ();  // 将⾃⼰添加到订阅器的操作
}
Watcher.prototype = {
update: function() {
this.run();
},
run: function() {
var value = this.vm.p];
var oldVal = this.value;
if (value !== oldVal) {
this.value = value;
this.cb.call(this.vm, value, oldVal);
}
},
get: function() {
Dep.target = this;  // 缓存⾃⼰
var value = this.vm.p]  // 强制执⾏⾥的get函数
Dep.target = null;  // 释放⾃⼰
return value;
}
};
在我研究代码的过程中,我觉得最复杂的就是理解这些函数的参数,后来在我输出了这些参数之后,函数的这些功能也容易理解了。vm,就是之后要写的SelfValue对象,相当于Vue中的new Vue的⼀个对象。exp是node节点的v-model或v-on:click等指令的属性值。如v-model="name",exp就是"name"。cb,就是Watcher绑定的更新函数。 上⾯的代码中就可以看出来,在Watcher的getter函数
中,Dep.target指向了⾃⼰,也就是Watcher对象。在getter函数中,
var value = this.vm.p]  // 强制执⾏⾥的get函数。
这⾥获取vm.p] 时,会调⽤Observer中Object.defineProperty中的get函数
Compile主要的作⽤是把new SelfVue 绑定的dom节点,(也就是el标签绑定的id)遍历该节点的所有⼦节点,出其中所有的v-指令 1.
<!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属性的值。 然后初始化⼀个解析器Compile,绑定这个节点,并解析其中的
v-,{{ }}指令
(每⼀个指令对应⼀个Watcher)并初始化模板数据以及初始化相应的订阅者,并把订阅者添加到订阅器中(Dep)。这样就实现双向绑定了。 如果v-model绑定的元素,
<input v-model="name">

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。