Observer.js源码分析
分析源码之前先要知道observer是什么,observer其实就是充当着一个订阅者模式,当对象状态发生改变时会通知观察者,模式适用于根据对象状态进行相应处理的场景。. Observer 并非主动观察,而是被动观察,通常情况下:. 一个Model对应多个View,这里也是使用Observer设计模式最多的地方.
温馨提示:有什么不懂就打印出来看打印结果尝试理解,我自己觉得这样效果还不错
(接下来我会详解这个代码块,你这需要顺着思路理解就好了)
以这个组件模块为例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MVVM</title>
</head>
<body>
<div id="app">
<h2>{{name}}</h2>
<button v-on:click="show" id="btn">提示</button>
</div>
<script src="./mvvm-master/js/observer.js"></script>
<script src="./mvvm-master/js/compile.js"></script>
<script src="./mvvm-master/js/watcher.js"></script>
<script src="./mvvm-master/js/mvvm.js"></script>
<script>
/**
* 模版解析: 事件指令解析
* 1. 从元素节点中取出指令
* 2. 从指令名中取出事件名
* 3. 根据指令的值(表达式)从methods中得到对应的回调函数
* 4. 给当前元素节点绑定指定的事件名和回调函数(指定this指向为vm)
* 5. 移除元素的指令属性
*/
const vm = new MVVM({
el: "#app",
data: {
name: "aa"
},
methods:{
show(){
alert("这是一个提示" + this.name)
}
}
});
function show(){
alert("这是一个提示" + this.name)
}
show();
var btn = document.getElementById("btn");
btn.addEventListener("click", show, false)
</script>
</body>
</html>
observer.js代码如下
function Observer(data) {
//在Observer实例上暂存data
//此时的data为Observer原型上的data对象
console.log(this, data);//打印信息为this:Observer {} data:{name: "aa", age: {…}}
//所以this.data值就为{name: "aa", age: {…}}
this.data = data;
this.walk(data);
打印信息如下:
Observer原型上有walk,convert,defineReactive三个方法,在Obesever原型中引入convent方法为每个属性增加响应式,通过调用observer原型中的definReactive方法来为每个属性增加响应式,为data中所有层次的属性都创建一个dep实例,
//Observer原型上有walk,convert,defineReactive三个方法
Observer.prototype = {
walk: function (data) {
console.log(Observer.prototype);
var me = this;//指向observer的原型
//把伪数组转换为数组对里面所有的属性名进行遍历
Object.keys(data).forEach(function (key) {
//在Obesever原型中引入convent方法为每个属性增加响应式
me.convert(key, data[key]);
});
},
convert: function (key, val) {
//通过调用observer原型中的definReactive方法来为每个属性增加响应式,
this.defineReactive(this.data, key, val);
},
defineReactive: function (data, key, val) {
//{name: "aa"} "name" "aa"
console.log(data, key, val);
//为data中所有层次的属性都创建一个dep实例
//Dep是data每个对象包括子对象都拥有一个该对象, 当所绑定的数据有变更时, 通过dep.notify()通知Watcher
var dep = new Dep();
//递归遍历data中所有层次的属性
var childObj = observe(val);
console.log(childObj);
//为data中所有层次的属性都创建一个dep实例
Object.defineProperty(data, key, {
enumerable: true, // 可枚举
configurable: false, // 不能再define默认配置为false
get: function () {
console.log(Dep.target);
//当Dep.target存在且为null的时候执行dep.depend()
if (Dep.target) {
dep.depend();
// Dep {id: 0, subs: Array(1)}
console.log(dep);
}
return val;
},
//监听当前属性值发生改变的时候触发回调函数
set: function (newVal) {
if (newVal === val) {
return;
}
val = newVal;
// 新的值是object的话,进行监听
childObj = observe(newVal);
//通过dep.notify 通知订阅者
dep.notify();
}
});
}
};
打印信息如下:
判断value是否存在或者value的数据类型是否为object(递归的终止条件)
function observe(value, vm) {
//判断value是否存在或者value的数据类型是否为object(递归的终止条件)
if (!value || typeof value !== 'object') {
return;
}
return new Observer(value);
};
Dep是订阅者watcher对应的数据依赖每个Dep都有唯一的ID,subs用于存放依赖,想subs数组添加依赖移除依赖,设置某个watcher的依赖这里添加了Dep.target是否存在判断,目的是判断是不是watcher的构造函数调用也就是说判断他是watcher的this.get调用的,而不是普通调用
function Dep() {
//没创建一个dep都会给这个dep增加一个独立的标识
//每个Dep都有唯一的id
this.id = uid++;
//subs用于存放依赖
this.subs = []; //watcher
}
Dep.prototype = {
//向subs数组添加依赖
addSub: function (sub) {
console.log(sub);
this.subs.push(sub);
},
//设置某个watcher依赖这里添加了Dep.target是否存在判断,目的是判断是不是watcher的
// 构造函数调用也就是说判断他是watcher的this.get调用的,而不是普通调用
depend: function () {
console.log(this);
if (Dep.target) {
Dep.target.addDep(this);
}
},
//向subs数组移除依赖
removeSub: function (sub) {
var index = this.subs.indexOf(sub);
if (index != -1) {
this.subs.splice(index, 1);
}
},
//通知所有绑定的watcher,调用watcher的update()
notify: function () {
//遍历subs中所有的watcher的实例
this.subs.forEach(function (sub) {
// 每一个watcher的实例调用update方法
sub.update();
});
}
};
Dep.target = null;
打印信息如下:
写到这里关于Observer.js中的一些知识就结束了,后面会陆续更新vue中的其他源码知识,下期再见!