一、watch侦听器的特点:
1、关注点在数据改变时,内部逻辑的处理,就是要做一点什么。
2、当依赖的数据发生改变时,执行watch中对应的方法。
class Vue {
constructor(options) {
const { data, computed, watch } = options;
this.$data = data();
this.init(this, computed, watch);
}
init(vm, computed, watch) {
this.initData(vm);
this.initComputed(vm, computed);
this.initWatch(vm, watch);
}
initData(vm) {
this.reactiveData(vm, (key, value) => {
// console.log(key, value);
}, (key, newValue, oldValue) => {
if(newValue===oldValue) return
this.$computed.update(key,this.$watch.invoke.bind(this.$watch))
this.$watch.invoke(key, newValue, oldValue)
})
}
reactiveData(vm, getCallback, setCallback) {
var _data = vm.$data;
for (let key in _data) {
Object.defineProperty(vm, key, {
get() {
getCallback(key, _data[key]);
return _data[key];
},
set(newValue) {
const oldValue = _data[key];
_data[key] = newValue;
setCallback(key, newValue, oldValue);
}
})
}
}
initComputed(vm, computed) {
this.$computed = new Compute()
for (let key in computed) {
this.$computed.reactiveCompute(vm, key, computed)
}
}
initWatch(vm, watch){
this.$watch = new Watch()
for (let key in watch) {
this.$watch.reactiveWatch(vm, key, watch)
}
}
}
class Compute {
constructor() {
this.computeData = []
}
reactiveCompute(vm, key, compute) {
var descriptor = Object.getOwnPropertyDescriptor(compute, key),
fn = descriptor.value.get ? descriptor.value.get : descriptor.value,
value = fn.call(vm),
get = fn.bind(vm),
dep = this.collectionDep(fn);
this.addCompute({ key, value, get, dep });
this.reactive(vm, key);
}
reactive(vm, key) {
var item = this.computeData.find(item => item.key === key)
Reflect.defineProperty(vm, key, {
get() {
return item.value
},
set(newValue) {
item.value = item.get()
}
})
}
addCompute(prop) {
this.computeData.push(prop)
}
collectionDep(fn) {
var collection = fn.toString().match(/this.(.+?)/g);
collection = collection.map((item) => item.split('.')[1]);
return collection;
}
update(key,watch) {
for (let item of this.computeData) {
if (item.dep.includes(key)) {
const oldValue=item.value;
item.value = item.get();
watch(item.key,item.value,oldValue)
return
}
}
}
}
class Watch{
constructor() {
this.Watchers=[]
}
reactiveWatch(vm, key, watch){
var fn=watch[key].bind(vm);
this.addWatch({key,fn})
}
addWatch(prop){
this.Watchers.push(prop)
}
invoke(key, newValue, oldValue){
for (let item of this.Watchers) {
if (item.key===key) {
item.fn(newValue, oldValue)
return
}
}
}
}
var options = {
data() {
return {
a: 1,
b: 2,
c:3
}
},
computed: {
total() {
console.log('Computed');
return this.a + this.b+this.c;
}
},
watch: {
total(newValue, oldValue) {
console.log('total', newValue, oldValue);
},
a(newValue, oldValue) {
console.log('a', newValue, oldValue);
},
b(newValue, oldValue) {
console.log('b', newValue, oldValue);
}
}
}
const vm = new Vue(options);
console.log(vm);
console.log(vm.total);
vm.c = 15;
console.log(vm.total);