让数据变得可观测 我们都知道 Vue2 中是通过Object.defineProperty来实现的数据劫持,也就是让数据变得可观测 例如:
let  car = {};let  val = 3000 ;Object .defineProperty (car, "price" , {  enumerable : true ,   configurable : true ,   get ( ) {     console .log ("price属性被读取了" );     return  val;   },   set (newVal ) {     console .log ("price属性被修改了" );     val = newVal;   }, }); 
 
依赖收集 让 object 数据变的可观测。变的可观测以后,我们就能知道数据什么时候发生了变化,那么当数据发生变化时,我们去通知视图更新就好了。那么问题又来了,视图那么大,我们到底该通知谁去变化?总不能一个数据变化了,把整个视图全部更新一遍吧,这样显然是不合理的。此时,你肯定会想到,视图里谁用到了这个数据就更新谁呗。对!你想的没错,就是这样.
在 Vvue 中是通过实现的一个 Dep 类来实现依赖收集 源码如下
export  default  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 );     }   }      notify ( ) {     const  subs = this .subs .slice ();     for  (let  i = 0 , l = subs.length ; i < l; i++) {       subs[i].update ();     }   } } export  function  remove (arr, item ) {  if  (arr.length ) {     const  index = arr.indexOf (item);     if  (index > -1 ) {       return  arr.splice (index, 1 );     }   } } 
 
在上面的依赖管理器 Dep 类中,我们先初始化了一个 subs 数组,用来存放依赖,并且定义了几个实例方法用来对依赖进行添加,删除,通知等操作。
有了依赖管理器后,我们就可以在 getter 中收集依赖,在 setter 中通知依赖更新了,代码如下:
function  defineReactive (obj, key, val ) {  if  (arguments .length  === 2 ) {     val = obj[key];   }   if  (typeof  val === "object" ) {     new  Observer (val);   }   const  dep = new  Dep ();    Object .defineProperty (obj, key, {     enumerable : true ,     configurable : true ,     get ( ) {       dep.depend ();        return  val;     },     set (newVal ) {       if  (val === newVal) {         return ;       }       val = newVal;       dep.notify ();      },   }); } 
 
在上述代码中,我们在 getter 中调用了 dep.depend()方法收集依赖,在 setter 中调用 dep.notify()方法通知所有依赖更新
Watcher 谁用到了数据,谁就是依赖,我们就为谁创建一个Watcher实例。在之后数据变化时,我们不直接去通知依赖更新,而是通知依赖对应的 Watch 实例,由Watcher实例去通知真正的视图
export  default  class  Watcher  {  constructor (vm, expOrFn, cb ) {     this .vm  = vm;     this .cb  = cb;     this .getter  = parsePath (expOrFn);     this .value  = this .get ();   }   get ( ) {     window .target  = this ;     const  vm = this .vm ;     let  value = this .getter .call (vm, vm);     window .target  = undefined ;     return  value;   }   update ( ) {     const  oldValue = this .value ;     this .value  = this .get ();     this .cb .call (this .vm , this .value , oldValue);   } } const  bailRE = /[^\w.$]/ ;export  function  parsePath (path ) {  if  (bailRE.test (path)) {     return ;   }   const  segments = path.split ("." );   return  function  (obj ) {     for  (let  i = 0 ; i < segments.length ; i++) {       if  (!obj) return ;       obj = obj[segments[i]];     }     return  obj;   }; } 
 
Watcher先把自己设置到全局唯一的指定位置(window.target),然后读取数据。因为读取了数据,所以会触发这个数据的 getter。接着,在 getter 中就会从全局唯一的那个位置读取当前正在读取数据的 Watcher,并把这个 watcher 收集到 Dep 中去。收集好之后,当数据发生变化时,会向 Dep 中的每个 Watcher 发送通知。通过这样的方式,Watcher 可以主动去订阅任意一个数据的变化
以上,就彻底完成了对 Object 数据的侦测,依赖收集,依赖的更新等所有操作