我的回答
- 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,这样已经完成了一个依赖收集的过程