学习watch原理之前需要了解更新原理
首先清楚在vue中watch有几种常见用法
let vm = new Vue({
el:'#app',
data(){
return {
name:'123'
}
},
methods:{
a(){
}
},
watch:{
// 第一种
name(newVal,oldVal){
console.log(newVal,oldVal);
},
// 第二种
name:[
function (newValue,oldVal){
console.log(newValue,oldValue);
},
function (a,b){
console.log(a,b);
}
],
//第三种
name:'a'
}
})
// 第四种
vm.$watch('name',(a,b)=>{
console.log(a,b,'---$watch');
})
//更改值,监听变化
setTimeout(()=>{
vm.name = '455'
},1000)
初始化watch时 需要看一下watch的所有属性和他的值,如果他的属性是数组,需要把数组拆分开,生成多个watcher ,watcher越多,性能越不好,如果只有一个值,直接创建watcher
function initWatch(vm, watch) {
for (let key in watch) {
let handler = watch[key];
if (Array.isArray(handler)) {
for (var i = 0; i < handler.length; i++) {
createWatcher(vm, key, handler[i]);
}
} else {
createWatcher(vm, key, handler);
}
}
}
function createWatcher(vm, key, handler) {
return vm.$watch(key, handler);
}
创建watcher 等价于调用vm.$watcher,$watcher核心就是创建watcher,如果用户传入immediate会,立即执行回调
function createWatcher(vm, key, handler) {
return vm.$watch(key, handler);
}
export function stateMixin(Vue) {
Vue.prototype.$watch = function (key, handler,options={}) {
options.user = true;
new Watcher(this, key,handler, options);
if(options.immediate){
handler(watcher.value)
}
};
}
进入watcher后,用户回调绑定到cb上,user属性绑定到user上,根据" . " 分割,去vm上面取值,因为需要监控这个值的变化,所以要把watcher和这个属性关联起来,默认会先去调用一次get方法,调用get时,可以拿到初始化的值,初始化的值保存到了this.value上,稍后用户更新会走run方法,run方法再次调用get方法,会拿到一个新值,有了新值和老值之后,如果是用户watcher,需要让用户传入的回调去执行,用户回调是cb
class Watcher {
constructor(vm, exporOrFn, cb, options={}) {
this.vm = vm;
this.exporOrFn = exporOrFn;
this.options = options;
this.cb = cb;
this.id = id++;
this.user = !!options.user;//是否是用户调用
this.deps = [];
this.depsId = new Set();
//默认应该让exportOrFn执行,exportOrFn做了什么事?去render上取值
if (typeof exporOrFn == "string"){
this.getter = function (){//需要将表达式转化成函数
//当我数据取值时,会进行依赖收集
// age.n vm['age.n'] => vm['age']['n']
let arr = exporOrFn.split('.');
let obj = vm;
for (var i = 0;i<arr.length;i++){
obj = obj[arr[i]]
}
return obj;//getter
}
}else{
this.getter = exporOrFn;
}
//value 值是第一次渲染的value
this.value= this.get(); //默认初始化要取值
}
get() {
//稍后用户更新时,可以重新调用getter方法
//defineProperty.get,每个属性都可以收集自己的watcher,每个属性都有一个依赖收集的对象 dep
//一个属性可以对应多个watcher,一个watcher也可以对应多个属性
pushTarget(this);
const value = this.getter();
popTarget(this);
return value;
}
run(){
this.newValue = this.get();
this.oldValue = this.value
this.value = this.newValue;//为了保证这一次的新值是下一次的老值
if(this.user){
this.cb.call(vm,this.newValue, this.oldValue);
}
}
}