一直想研究一下vue的源码,但苦于没有耐力坚持下去,所以就一直拖着,今天就来研究一下吧,事情总是要做的,闲话少叙,我们都知道,vue双向绑定的核心是通过Object.defineProperty()来劫持数据,再结合设计模式发布/定义来设计的,那么什么叫双向绑定呢,双向绑定意味着视图发生改变,数据也发生改变,数据发生改变,视图也会发生改变,双向绑定的,如下图所示:
Object.defineProperty()是什么意思呢?,来个例子。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Object.defineProperty</title>
</head>
<body>
<script>
let obj = {
}
let book = ""
Object.defineProperty(obj, "book", {
get: function () {
return book
},
set: function (newVal) {
book = newVal + "啦啦"
}
})
obj.book = 'Vue.js实战'
console.log(obj.book)
console.log(obj)
</script>
</body>
</html>
从上面可以看到多了下面几个属性 get,set ,而我们去看一下vue里面也具备同样的get,set属性,不妨可以去试一下,这里不过多阐述。
// 引入vue文件 new一个Vue对象
var vm = new Vue({
el: '#app',
// beforeCreate() {
// console.log(1)
// },
created() {
console.log(this.msg)
},
data: {
msg: 'hello vue'
}
})
function observe(value, asRootData) {
// value是上面 { msg: 'hello vue'} asRootData一个flag 是不是作为根数据
if (!isObject(value) || value instanceof VNode) {
// 传过来的value是否是纯粹的对象 或者 value的原型是否是VNode虚拟节点 ,如果满足条件 return
return
}
var ob;
// value中是否有 __ob__属性且他的原型是否是Observer类
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__;
} else if ( // 服务器渲染相关 暂且不谈
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
// 开始观察数据
ob = new Observer(value);
}
if (asRootData && ob) {
ob.vmCount++;
}
return ob
}
var Observer = function Observer(value) {
// value: { msg: 'hello vue'}
this.value = value; // 将value放到Observer上
this.dep = new Dep(); // 消息管理中心
/*
var Dep = function Dep() {
this.id = uid++;
this.subs = []; // 观察数组列表
};
*/
this.vmCount = 0;
def(value, '__ob__', this); // 将this赋值给__ob__属性并挂载到value上
if (Array.isArray(value)) { // 判断value是不是数组
if (hasProto) {
protoAugment(value, arrayMethods);
} else {
copyAugment(value, arrayMethods, arrayKeys);
}
this.observeArray(value);
} else {//value是对象 执行下方的walk
this.walk(value);
}
};
Observer.prototype.walk = function walk(obj) {
// obj:{ msg: 'hello vue'}
var keys = Object.keys(obj); // 获取obj的键 这里是"msg"
for (var i = 0; i < keys.length; i++) {//循环
defineReactive$$1(obj, keys[i]);
}
};
function defineReactive$$1(
obj, // { msg: 'hello vue'}
key, // "msg"
val,
customSetter,
shallow
) {
var dep = new Dep();
var property = Object.getOwnPropertyDescriptor(obj, key); // 获得修饰符
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
var getter = property && property.get;
var setter = property && property.set;
if ((!getter || setter) && arguments.length === 2) {
val = obj[key]; // val = "hello vue"
}
/*
这里是重点,因为对象中可以内嵌对象,要对每一个属性就行数据劫持,就应该使用递归
假设我们前面传过来的是下方的格式,那么上面一行应该是 val = obj[key];
val应该是 {book: "Vue.js", message: {test: 123}} 递归调用observe(val)
而 observe方法中有这个判断是否为纯粹对象 book不是纯粹对象 直接返回 所以childOb = false,
而message照着上面的循坏 递归一下 observe =>Observe继续调用,下面画一个流程图,让大家看的更清楚
if (!isObject(value) || value instanceof VNode) {
// 传过来的value是否是纯粹的对象 或者 value的原型是否是VNode虚拟节点 ,如果满足条件 return
return
}
data() {
return {
msg: {
book: "Vue.js",
message: {
test: 123
}
}
}
}
*/
var childOb = !shallow && observe(val); // 递归val
Object.defineProperty(obj, key, { // 开始劫持数据
enumerable: true,
configurable: true,
get: function reactiveGetter() {
// 如果getter不为空 则调用getter的方法,否则使用val
var value = getter ? getter.call(obj) : val;
if (Dep.target) { //如果消息管理中心中target不为空执行下方,这里的target就要配合Watcher使用
// ,后面再讲
dep.depend();
if (childOb) {
childOb.dep.depend();
if (Array.isArray(value)) {
dependArray(value);
}
}
}
return value
},
set: function reactiveSetter(newVal) {
var value = getter ? getter.call(obj) : val;
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (customSetter) {
customSetter();
}
// #7981: for accessor properties without setter
if (getter && !setter) { return }
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = !shallow && observe(newVal);
dep.notify();
}
});
}
上面的数据劫持get set 讲到Watcher的时候再详细讲,到此 Observe已经讲完了
function Watcher(vm, exp, cb) {
this.cb = cb;
this.vm = vm;
this.exp = exp;
// 此处为了触发属性的getter,从而在dep添加自己
this.value = this.get();
}
调用get
Watcher.prototype.get = function get() {
pushTarget(this); // 推入栈中
var value;
var vm = this.vm;
try {
value = this.getter.call(vm, vm); // 这句关键 会触发前面的数据拦截里面的get
} catch (e) {
if (this.user) {
handleError(e, vm, ("getter for watcher \"" + (this.expression) + "\""));
} else {
throw e
}
} finally {
// "touch" every property so they are all tracked as
// dependencies for deep watching
if (this.deep) {
traverse(value);
}
popTarget();
this.cleanupDeps();
}
return value
};
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
var value = getter ? getter.call(obj) : val;
if (Dep.target) {
dep.depend(); // 判断是否存在订阅者 不存在 添加订阅者
if (childOb) {
childOb.dep.depend();
if (Array.isArray(value)) {
dependArray(value);
}
}
}
return value
},
set: function reactiveSetter(newVal) {
var value = getter ? getter.call(obj) : val;
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) { // 如果值未发生改变 则返回
return
}
/* eslint-enable no-self-compare */
if (customSetter) {
customSetter();
}
// #7981: for accessor properties without setter
if (getter && !setter) { return }
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = !shallow && observe(newVal);
dep.notify();
}
});
Dep.prototype.depend = function depend() {
if (Dep.target) {
Dep.target.addDep(this);
}
};
Watcher.prototype.addDep = function addDep(dep) {
var id = dep.id;
if (!this.newDepIds.has(id)) {
this.newDepIds.add(id);
this.newDeps.push(dep);
if (!this.depIds.has(id)) { //如果订阅者不存在 则添加订阅者
dep.addSub(this);
}
}
};
Dep.prototype.addSub = function addSub(sub) {
this.subs.push(sub); // 往subs数组添加数据 添加订阅者
};
这篇文章确实不错,可以参考一下。