constdata={flag:false,firstName:'郑',lastName:'沂蒙'}constcomputed={name(){if(!data.flag){return'你拿不到'}returndata.firstName+''+data.lastName},}functionobserve(obj){constkeys=Object.keys(obj)/**需要付费这里注意,我没有对传入的obj本身进行响应式处理,以简化代码(vue源码也对传入的对象进行处理)*/keys.forEach(key=>{observer(obj,key)})}functionobserver(obj,key){letvalue=obj[key]//?1为什么需要这个东西letdep=newDep()//每个属性都有自己的dep。Object.defineProperty(obj,key,{get(){if(Dep.target){console.log(`collectiondependencies${key}`)Dep.target.addDep(dep)}returnvalue//?1因为在这里写obj[key]相当于重新访问这个key,会再次触发get方法,进入死循环},set(newVal){if(value===newVal)returnvalue=newValif(dep.subs.length){console.log(`${key}已经改变,我想更新它记录的watcher`)dep.notify()}}})}functioninitComputed(computed){/**注意我在做计算这里重新反汇编只是为了让大家明白,vue的源码和我的不一样,但是原理是差不多的*/constwatchers={}constkeys=Object.keys(computed)keys.forEach(key=>{watchers[key]=newWatcher(computed[key])})returnwatchers}functionDep(){this.subs=[]}Dep.prototype.notify=function(){this.subs.forEach(watcher=>watcher.update())}functionWatcher(func){this.get=functhis.deps=[]Dep.target=thisthis.value=this.get()//函数体是{returndata.firstName+''+data.lastName},所以会调用data.firstName和data.lastName的get方法Dep.target=null}Watcher.prototype.addDep=function(dep){//这里为什么要记录dep?因为:假设现在flag为true,那么我们的computed.name在采集dependencies时采集数据.flag,data.firstName,data.lastName,这三个属性每一个都有一个dep,修改其中一个会重新调用dep。notify()来更新name值,但是当我们修改其中一个属性时,这个收集到的依赖实际上已经改变了,比如修改flag=false,然后修改data.firstName和data.lastName,应该不会重新计算值computed.name的,因为你无法到达那一步,然后数据。firstName和data.lastName的dep应该是空的,这样在修改data.firstName和data.lastName的时候,computed.name的值不会因为dep是空的而重新计算。this.deps.push(dep)部门。subs.push(this)//还记得我们是什么时候到这里的吗?自身属性的watcher被记录}Watcher.prototype.update=function(){this.deps.forEach(dep=>dep.subs=[])//这里,计算属性对应的每个依赖属性都是depsclearedDep.target=thisletvalue=this.get()//这里调用get重新收集依赖dep.target=nullif(this.value!==value){this.value=value//valueChange,渲染页面}}functionMyVue({data,computed}){observe(data)this.watchers=initComputed(computed)//这里简单地将属性绑定到实例本身Object.keys(this.watchers).forEach(key=>{//computed这里假设不允许手动修改,所以不会处理Object.defineProperty(this,key,{get(){返回this.watchers[key].value}})})Object.keys(data).forEach(key=>{Object.defineProperty(this,key,{get(){returndata[key]},set(newVal){data[key]=newVal}})})}window.vm=newMyVue({data,computed})初始化dataobserve(data)functionobserve(obj){constkeys=Object.keys(obj)/**这里需要注意,我没有对传入的obj进行响应式处理,本身就是为了简化代码(vue源码也对传入的对象进行了处理)*/keys.forEach(key=>{observer(obj,key)})}functionobserver(obj,key){letvalue=obj[key]//存储旧值letdep=newDep()//每个属性都有自己的depObject.defineProperty(obj,key,{get(){if(Dep.target){console.log(`collectiondependencies${key}`)Dep.target.addDep(dep)}returnvalue//?1因为在这里写obj[key]相当于重新访问这个key,会再次触发get方法,进入死循环},set(newVal){if(value===newVal)returnvalue=newValif(dep.subs.length){console.log(`${key}变了,我要更新它记录的watcher`)dep.notify()}}})}为每个数据属性生成一个dep对象,在attribute,触发watch.addDep方法完成dep和watch的相互收集。在set方法中通知watchDep对象subs:storewatchnotify:属性更新提醒相关watchfunctionDep(){this.subs=[]}Dep.prototype.notify=function(){this.subs.forEach(watcher=>watcher.update())}Initializecomputedthis.watchers=initComputed(computed)functioninitComputed(computed){/**注意我这里重新反汇编了computed只是为了让大家明白,vue的源码和我的不一样,但原理类似*/constwatchers={}constkeys=Object.keys(computed)keys.forEach(key=>{watchers[key]=newWatcher(computed[key])})returnwatchers}生成watch对于每个计算。watchfunctionWatcher(func){this.get=functhis.deps=[]Dep.target=thisthis.value=this.get()//函数体是{returndata.firstName+''+data.lastName},所以会调用data.firstName和data.lastName的get方法Dep.target=null}Watcher.prototype.addDep=function(dep){//这里为什么要记录dep呢?因为:假设现在flag为true,那么我们计算的.name在收集依赖的时候收集了data.flag、data.firstName和data.lastName。这三个属性中的每一个都有一个dep。修改其中一个会再次调用dep.notify()来更新name值,但是当我们修改了其中一个属性时,集合的依赖关系实际上已经改变了。比如修改flag=false,修改data.firstName和data.lastName的时候,computed.name的值不要重新计算,因为如果到不了那一步,那么data.firstName和data.lastName的depdata.lastName应该为空,这样修改data.firstName和data.lastName时,computed.name的值不会因为它们的dep为空而重新计算。.deps.push(dep)dep.subs.push(this)//到了这里,还记得吗?自身属性的watcher被记录}Watcher.prototype.update=function(){this.deps.forEach(dep=>dep.subs=[])//这里计算出的属性对应的每个依赖属性的deps是都清空了,这段代码还是有点问题,其他的监听也会被去掉dep.target=thisletvalue=this.get()//这里调用getdep.target=nullif(this.value!==value){this.value=value//值变化后重新收集依赖,渲染页面}}deps:存储相关依赖dep1,调用this.get在constructor方法中会触发相关属性的get方法,然后触发watch.addDep方法,dep和watch会相互收集。2.当依赖更新时,会触发watch.update。清空最后一个依赖数组;重新计算计算值;重建依赖数组
