Vue源码之对象的侦测变化
最近在看深入浅出Vue.js,看了比较重要的前几章节,关于Vue的响应式原理,做几点总结。
1.要追踪对象的属性的修改变化,可以用es5的Object.defineProperty(obj,key,descriptor)和es6的proxy;Vue使用的是Object.defineProperty。
在修改或获取属性值的时候就会触发setter和getter函数,vue就是在这两个函数去收集和触发与变量有关的依赖的。
function defineReactive(obj,key,val){ Object.defineProperty(obj,'name',{ enumerable:true, configurable:true, get:function(){ return val }, set:function(newVal){ if(val === newVal) return ; console.log('shezhi1') val = newVal } }) }
2.为了降低耦合性,vue把收集依赖这个操作封装成一个类---Dep。Dep会收集和删除依赖,以及通知依赖属性发生改变需要相关的更新操作等。
class Dep{ constructor(){ this.subs = []; } addSub(sub){ this.subs.push(sub) } removeSub(sub){ remove(this.subs,sub) } depend(){ if(window.target){ this.addSub(window.target) } // window.target = undefined } notify(){ if(this.subs.length){ for(var i =0;i<this.subs.length;++i){ this.subs[i].update() } } } }
这里的window.target就是依赖,依赖就是watcher,当属性值改变时通知的就是watcher,而watcher也是一个类
3.Watcher就是依赖,响应式数据的触发更新都是通过watcher的。
class Watcher{ constructor(vm,expOrFn,cb){ this.vm = vm; //vm是vue实例,expOrFn,cb类似于$watch的第一、二个参数 if(typeof expOrFn === 'function'){ this.getter = expOrFn }else{ this.getter = parsePath(expOrFn) } this.cb = cb; this.value = this.get() } get(){ //这里把window.target设置为当前Watcher实例 window.target = this; //这里获取值就会触发getter let value = this.getter.call(this.vm) window.target = undefined; return value } //dep.notify()就会触发update update(){ //把旧的值存起来 const oldVal = this.value; //触发获取新的值 this.value = this.get(); //触发回调函数 this.cb.call(this.vm,this.value,oldVal) } }
4.defineReactive只是对单个属性的,为了让所有的响应式数据都能被绑定依赖,需要Observer,他也是一个类
//Observer将所有属性转换成 getter/setter 的形式来侦测变化,包含数组和对象 class Observer{ constructor(value){ this.value = value;if(Array.isArray(value)){ //数组 }else{ this.walk(value) } } //walk针对对象将所有属性转换成 getter/setter 的形式来侦测变化 walk(obj){ const keys = Object.keys(obj); for(let i=0,l=keys.length;i<l;++i){ defineReactive(obj,keys[i],obj[keys[i]]) } } }
这里defineReactive需要改变一下
function defineReactive(obj,key,val){ // 判断是不是对象,对象再进行Observer实例化 if(obj && Object.prototype.toString.call(obj)==='Object object'){ new Observer(obj) } let dep = new Dep(); Object.defineProperty(obj,key,{ enumerable:true, configurable:true, get:function(){ dep.depend(); return val }, set:function(newVal){ if(val === newVal) return; val = newVal; dep.notify() } }) }

浙公网安备 33010602011771号