经过上一节(响应式的创建过程)分析,我们知道了data中数据的每一层都被我们通过Object.defineProperty架设了一层拦截,当我们去访问的时候会先执行了一些自己逻辑。那么现在有两个问题:什么时候会访问到data?拦截后做了什么?
访问data中数据的时机
-经过之前章节的分析,我们知道,每一个组件的加载过程都要经历一次mount,在mountComponent过程中定义了updateComponent函数并实例化一个渲染watcher,在其constructor中将updateComponent赋值给getter并调用了get方法
调用pushTarget
(该方法向dep上的静态属性添加了当前的渲染watcher,同时向targetStack里push了一位)
~dep是我们说的"发布者",它将在适当的时机去向订阅者进行广播,而watcher其实就是它的订阅者
~向targetStack中push,主要是因为组件的渲染是一次嵌套渲染的过程,它需要在每一次渲染完子组件后恢复到父组件
接着便调用getter即updateComponent方法
~执行组件的render,将调用render.call,render其实就是我们在组件内部手写的render函数
(这里我们通过this.meta.msg进行了访问)
由于我们组件在init过程中已经为msg架设了拦截,故此时将进入msg的get方法
接着执行popTarget,将target恢复为父组件
(这是因为js是单线程的,子组件结束后vue需要接着去执行父组件的渲染流程)
最后执行cleanupDeps
(deps 、newDeps 、depIds 、newDepIds 都是在watcher实例化过程中定义的,这里是为了将某些watcher进行移除,减少不必要的更新。例如当结构中使用了v-if时,当其值为false时,我们就不需要也不应该再对其进行响应了)
~那么为什么说将这些deps 、 newDeps 、 depIds 、 newDepIds更新一遍后下次就不会再进行响应了呢?这是因为,每次get的时候都会调用dep.depend方法,dep.depend又会调用watcher的addDep方法,在该方法中对这些值进行了保存(即:完成了订阅),同时调用dep.addSub将本次的watcher保存到dep中(即:完成收集),那么不需要订阅的自然需要去除
拦截后做了什么
我们在分析cleanupDeps时实际上已经大致说了拦截后做的事情,那就是去完成watch的收集保存到dep的subs上,以便于在设置key的时候能向watcher进行广播,同时完成对dep的订阅保存到watcher的deps 、 newDeps 、 depIds 、 newDepIds 上,以便在适当的时机取消对dep的订阅









网友评论