defineProperty
定义基础数据function defineReactive(data, key, val) {
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function() {
return val;
},
set: function(newVal) {
if (val === newval) {
return;
}
val = newVal;
}
});
}
复制代码
在getter中触发依赖收集,在setter中触发依赖通知。javascript
function defineReactive(data, key, val) {
const dep = [];
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function() {
//依赖收集
dep.push(window.target);
return val;
},
set: function(newVal) {
if (val === newVal) {
return;
}
//依赖通知
for (let index = 0; index < dep.length; index++) {
dep[index].update(newVal, val);
}
val = newVal;
}
});
}
复制代码
Dep对象将负责收集、保存依赖,在属性变化时经过执行依赖的update()方法通知依赖。java
class Dep {
constructor() {
this.Deps = [];
}
addDep() {
if (window.target) {
this.Deps.push(window.target);
}
}
notify(newVal, val) {
for (let index = 0; index < this.Deps.length; index++) {
this.Deps[index].update(newVal, val);
}
}
}
复制代码
这里的window.target不只是依赖对象与Dep对象沟通的纽带,同时也能够经过判断window.target值在后续读取该属性避免屡次重复绑定依赖。函数
defineReactive
function defineReactive(data, key, val) {
let deps = new Dep()
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function() {
//依赖收集
deps.addDep();
return val;
},
set: function(newVal) {
if (val === newVal) {
return;
}
//依赖通知
deps.notify();
val = newVal;
}
});
}
复制代码
最终defineReactive函数将实现对传入对象属性的转换,使其成为响应式数据。然而defineReactive函数是以对象属性为单位进行转换,咱们更但愿传入对象直接将其全部属性转换。ui
class Observer {
constructor(value) {
this.value = value;
if (!Array.isArray(value)) {
this.travel(value);
}
}
travel(value) {
const keys = Object.keys(value);
for (let index = 0; index < keys.length; index++) {
defineReactive(value, keys[index], value[keys[index]]);
}
}
}
复制代码
什么是依赖?this
依赖是一个对象,它能够获取、保存绑定的属性值,该属性值发生变化时它的update方法将被触发,从而执行保存值的更新以及绑定的回调函数的执行。spa
依赖的用处?3d
经过依赖对象使外部代码能够与某属性产生关联,监听其变化并绑定回调函数。code
class Watcher {
constructor(obj, proOrObj, fn) {
this.obj = obj;
this.getter = this.parsePath(proOrObj);
this.fn = fn;
this.val = this.get();
}
get() {
if (!this.val) {
window.target = this;
}
let val = this.getter.call(this.obj, this.obj);
window.target = undefined;
return val;
}
update() {
let oldVal = this.val;
this.val = this.get();
this.fn(this.val, oldVal);
}
parsePath(path) {
const pros = path.split(".");
return function(obj) {
for (let index = 0; index < pros.length; index++) {
if (!obj) {
return;
}
obj = obj[pros[index]];
}
return obj;
};
}
}
复制代码
if (!this.val) {window.target=this;}
cdn
咱们须要将Watcher实例依赖添加到对象属性中,而在Dep对象中添加依赖都是经过window.target
,因此咱们须要将Watcher实例赋值给window.target
,而添加依赖只需在初次读取完成,后续若屡次读取该属性将重复添加相同依赖。server
let val=this.getter.call(this.obj,this.obj);
添加依赖是在属性的getter中执行因此咱们须要读取一次该属性,同时保存该属性值。这里的getter是parsePath()方法返回的一个函数,该函数接收一个对象经过递归解析该对象的嵌套属性路径(如:proOrObj = "a.b.c"
)并返回该属性值。
在属性的setter中咱们经过执行依赖的update()方法通知依赖,这里咱们将更新属性值而且执行回调函数。
const man = {};
defineReactive(man, "age", 1);
const watcher = new Watcher(man, "age", function(newVal, oldVal) {
console.log("age变化:新数据" + newVal + "老数据" + oldVal);
});
man.age = 2; //age变化:新数据2老数据1
man.age = 3; //age变化:新数据3老数据2
man.age = 4; //age变化:新数据4老数据3
man.age = 5; //age变化:新数据5老数据4
复制代码