// vm.$watch 和 watch
function initWtch(vm, watch) {
console.log(watch);
for (const key in watch) {
let handle = watch[key]
if (Array.isArray(handle)) {
for (let index = 0; index < handle.length; index++) {
const element = handle[index];
createWatcher(vm, key, element)
}
} else {
createWatcher(vm, key, handle)
}
}
}
export function stateMixin(Vue) {
Vue.prototype.$watch = function (key, handle,options={}) {
console.log(key, handle);
options.user = true // 用来区分这是用户watcher
// 创建渲染watcher
new Watcher(this,key,handle,options)
}
}
constructor(vm, exprFunc, callback, options) {
this.vm = vm;
//有可能为underfined 取bool值
this.user = !!options.user
// 这里判断一下如果exprFunc为string就是用户watcher
if (typeof exprFunc == 'string') {
this.exprFunc = function () {
// 'vm.a.a.a.b'
let arr = exprFunc.split(',')
let obj = arr.reduce((vm, item, index) => {
return vm[item]
}, vm)
return obj
}
// 这里取值完成后自动会将属性的dep在收集一个watcher,所以一个属性dep会对应多个watcher
}
else {
this.exprFunc = exprFunc;
}
this.callback = callback;
this.options = options;
// 自增标识符
this.id = id++;
this.watchers = [];
this.idSet = new Set();
// 自动执行render 这里会得到一个value值
this.value = this.getters();
}
//当渲染时自动会执行渲染
getters() {
//取值之前先把自己挂载到dep上的静态属性,类似于全局后面变量取值时会将dep和watcher关联
pushTarget(this);
const value = this.exprFunc();
popTarget();
return value
}
upData() {
// if(this)
let newValue = this.exprFunc();
let oldValue = this.value
this.value = newValue
// 属性改变时watch 只有user为true时获取值
if(this.user){
this.callback.call(this.vm,newValue,oldValue)
}
}
function createWatcher(vm, key, handle) {
return vm.$watch(key, handle)
}
watch的实现原理总结
-
当初始化watch的时候,循环watch,并且 new watcher() 然后watcher内部会将key编成函数取值赋值给变量,调用编写完的函数取值,然后将当前监听的watcher放到Dep.target上函数取值的结果付给watcher的value,当取值的时候会走到属性的getter函数,将每个属性上的dep属性进行watcher收集,并且watcher也收集当前dep,进行相互收集。
-
遍历完ast数后,执行render会取值,这里也会收集一遍渲染watcher
-
当属性改变后,回调用dep上的depend方法将对应的watchers进行相应的更新,这时,会重新取到最新的值,然后调用watcher的get方法,进行watch的回调并且重新执行render方法重新取值渲染视图