我的回答
- data中的数据使用Object.definPropertype进行劫持, 并且使用Dep.target获得watch, 如果遇到数组需要将数组的7个原型方法进行重写, 深层的字段要进行递归劫持
将watcher放到对应的dep中, 如果属性发生变化触发dep.notify(), 然后调用watcher的update方法
参考回答
Vue依赖收集
在初始化vue每个组件时,会对组件的data进行初始化,就会将由普通对象变成响应式对象,在这个过程中便会进行依赖收集的相关逻辑
function defieneReactive(obj,key,val){const dep=new Dep();//...Object.defineProperty(obj,key,{//...get:function reactiveGetter(){if(Dep.target){dep.depend();//...}return val}})}
上面的代码主要说明:const dep=new Dep() 实例化一个Dep实例,然后能在get函数中通过dep.depend() 进行依赖收集
Dep
Dep是整个依赖收集的核心
class Dep {static target;subs;constructor () {...this.subs = [];}addSub (sub) { //添加this.subs.push(sub)}removeSub (sub) { //移除remove(this.sub, sub)}depend () { //target添加if(Dep.target){Dep.target.addDep(this)}}notify () { //响应const subs = this.subds.slice();for(let i = 0;i < subs.length; i++){subs[i].update()}}}
Dep是一个class,里面有一个静态属性static,指向全局唯一的Watcher,保证了同一时间全局只有一个watcher被计算,另一个属性subs则是一个watcher数组,所以dep实际上就是对watcher的管理
watcher
class Watcher {getter;...constructor (vm, expression){...this.getter = expression;this.get();}get () {pushTarget(this);value = this.getter.call(vm, vm)...return value}addDep (dep){...dep.addSub(this)}...}function pushTarget (_target) {Dep.target = _target}
watcher是一个class,定义了一些方法,其中和依赖收集相关的函数是get、addDep
过程
在实例化Vue时,依赖收集的相关过程: 初始化状态 initState,这中间便会通过defineReactive将数据变成响应式对象,其中的getter部分便是用来收集的。
初始化最终会走mount过程,其中会实例化watcher,进入watcher中,便会执行this.get()方法 ```javascript updateComponent = ()=>{ vm._update(vm._render()) }
new Watcher(vm,updateComponent)
```
get方法中的pushTarget实际上就是把Dep.target赋值为当前的watcher,this.getter.call(vm,vm),这里的getter会执行vm._render()方法,在这个过程中便会触发数据对象的getter
那么每个对象值的getter都持有一个dep,在触发getter的时候会调用dep.depend()方法,也就是会执行Dep.target.addDep(this)
刚才Dep.target已经被赋值为watcher,于是就执行addDep方法,然后走到dep.addSub()方法,便将当前的watcher订阅到这个数据持有的dep的subs中,这个是为了后续数据变化的时候能通知到哪些subs做准备
所以在vm._render()过程中,会触发所有的数据的getter,这样已经完成了一个依赖收集的过程
