Vue原理中,最重要的部分就是如何实现数据观察、依赖收集、视图更新。这篇文章是关于Observer、Dep、Watcher的简单实现。pub(发布)表示发布者,sub(订阅)表示订阅者,cb(callback)表示回调函数如果觉得这篇文章对你有帮助,请帮我订一个starbserver来实现Observer的作用是为了让object对象的属性用Object.defineProperty()定义,这样在获取或修改对象的属性时,可以触发get和set,达到数据观察的效果。classObserver{constructor(value){this.value=valuethis.walk(this.value)}walk(value){//递归遍历value的属性Object.keys(value).forEach((key)=>{defineReactive(value,key,value[key])})}}functiondefineReactive(obj,key,val){letchildOb=observe(val)Obeject.defineProperty(obj,key,{enumerable:true,configurable:true,get(){console.log('')returnval},set(newVal){console.log('')val=newValchildOb=observe(val)}})}functionobserve(value){if(typeofvalue==='object'&&!Array.isArray(value)){value=newObserver(value)}}defineReactive的作用是对对象的属性进行简单的数据观察。一旦获取或设置值,就会触发一些行为。因为一个对象的属性可能仍然是对象,所以这里我们添加observe函数来遍历值,这样仍然可以观察到一个对象的属性的属性。简单的说就是可以忽略所有的属性。当然,在实际情况中,我们还需要考虑数组的情况,但都是大同小异的。这样做的代码似乎有点难看。当我们设置属性并触发设置时,就会发生console.log()函数。有没有更聪明的方式来实现通知的变化呢?这里我们需要使用消息订阅者来实现。这样我们就不需要观察console.log()的输出值就可以看到进度了。我们只需要在set方法中添加一个通知即可。一旦值发生变化,Dep的实现会收到外部值发生变化的通知:Dep的作用是收集属性值的变化,一旦触发set方法就更新视图。然后准备一个数组来收集它!下面是Dep的实现:潜艇。forEach((sub)=>{sub.update()//viewupdate})}}以上是Dep的简单实现,addSub的作用是添加订阅者,因为订阅者很多,我们需要使用一个array存储,notify()函数的作用是在set发生时进行通知,update()函数后面会在watcher中提到。实现Dep后,我们是否应该更改set()函数?下面是defineReactive()修改后的代码obj,key,{enumerable:true,configurable:true,get(){returnval},set(newVal){val=newValchildOb=observe(val)dep.notify()//因为数据改变了,我们将notifyDep}})}一旦set被触发,dep.notify()就会被调用。notify的作用是更新订阅者的遍历。Watcher的简单实现:watcher的作用是在状态改变时更新视图,我们可以假设classWatcher{constructor(vm,cb,expOrFn){this.vm=vm//This表示一个Vue实例this.cb=cb//这里需要考虑expOrFn是字符串或者函数的情况//这里做个简化,只考虑函数的情况this.getter=expOrFnthis.value=this.get()}get(){Dep.target=thisconstvm=this.vmvalue=this.getter.call(this.vm,vm)Dep.target=null返回值}update(){this.run()}run(){constvalue=this.get()if(value!==this.value){constoldValue=this.valuethis.value=valuethis.cb.call(this.vm,value,oldValue)}}}简单的实现Watcher的完成,在Dep()构造函数中,我们使用了sub.update()这行代码,update函数是Watcher中的一个方法,说明每一个sub都是Wathcer的一个实例。问题是我们应该如何通过addSub()方法,将Watcher添加到subs数组中并小心存储。答案是在defineReactive()中修改functiondefineReactive(obj,key,val){letdep=newDep()//毕竟要用到Dep的方法letchildOb=observe(val)Obeject.defineProperty(obj,key,{enumerable:true,configurable:true,get(){if(Dep.target){dep.addSub(Dep.target)}returnval},set(newVal){}val=newValchildOb=observe(val)dep.notify()//因为数据有变化,我们会通知Dep}})}是否可以在Dep中加入Watcher?vue源码比这个复杂多了,各种参数,看着头大,本文的目的就是通过简化让大家明白内部原理。如果需要更深入的理解,需要阅读源码。
