vue简版源码 Observer篇
相关链接:MVVM篇 、 Compile篇 、Watcher篇
这篇代码的入口在中间 function observe(value, vm)
的时候。
function Observer(data) {
//在Observer实例上暂存data
this.data = data;
// 调用Observer 里 walk 方法 ,这里用的是颗粒化开发思想
this.walk(data);
}
Observer.prototype = {
walk: function(data) {
var me = this; // 暂存this 保证this的指向正确 这里的this是 Observer 实例
//对data里所有的属性名进行遍历
Object.keys(data).forEach(function(key) {
// 调用Observer 里 convert方法 ,这里用的是颗粒化开发思想
me.convert(key, data[key]); // 参数一:每一项属性名 , 参数二:data中每一项的属性值
});
},
convert: function(key, val) {
// 为每个属性增加响应式
// 参数一:说到底还是传递过来的那个data对象,一直没变 ,
// 参数二:每一项的属性名
// 参数三:每一项的属性值 可能是值类型,也可能是对象的形式
this.defineReactive(this.data, key, val);
},
defineReactive: function(data, key, val) {
//为data中所有层次的属性都创建一个dep实例
var dep = new Dep();
// 递归遍历data中所有层次的属性
// 这就是我在上面说的参数三,可能是对象形式,大家可以去下面看一下递归的终止条件 直到它不是存在并且不是object类型
var childObj = observe(val);
// 为原有属性新增get和set方法(数据劫持)
Object.defineProperty(data, key, {
enumerable: true, // 可枚举
configurable: false, // 不能再define
get: function() {
// 在写操作的时候如果有订阅者,调用depend 方法
if (Dep.target) {
dep.depend();
}
return val;
},
set: function(newVal) {
if (newVal === val) {
return;
}
val = newVal;
// 新的值是object的话,进行监听
childObj = observe(newVal);
// 通知订阅者
dep.notify();
}
});
}
};
// 入口
function observe(value, vm) {
//判断value是否存在或者value的数据类型是否为object(递归的终止条件)
if (!value || typeof value !== 'object') {
return;
}
return new Observer(value);
};
var uid = 0;
function Dep() { // 构造函数 挂载在 window 上
//没创建一个dep都会给这个dep增加一个独立的标识
this.id = uid++; // 从0开始
this.subs = []; // 存放订阅者的容器 watcher
}
Dep.prototype = {
// 在 订阅者上 watcher 触发
addSub: function(sub) { // sub : 订阅者 watcher
this.subs.push(sub); // 存放订阅者 watcher
},
depend: function() {
// Dep.target :是订阅者 watcher 调用 watcher 里 addDep 方法,参数this是Dep 本身
Dep.target.addDep(this);
},
removeSub: function(sub) {
var index = this.subs.indexOf(sub);
if (index != -1) {
this.subs.splice(index, 1);
}
},
//通知所有的watcher
notify: function() {
//遍历subs中所有的 watcher 的实例
this.subs.forEach(function(sub) {
// 每一个watcher的实例调用update方法
sub.update();
});
}
};
// 重点!!不能忘 在Dep构造函数上定义的属性,目前是null,在 watcher.js 中用的到
// 构造函数 挂载在window 上 在哪都能调用!!!
Dep.target = null;