依赖收集
当访问对象属性的时候,会触发定义的getter
在defineReactive
中,给属性添加的get
逻辑主要就是负责依赖收集
Dep
Dep
是一个类,是数据和watcher之间的桥梁
1 | // src/core/instance/observer/dep.js |
Dep.target
是当前的watcher
,唯一
Watcher
1 | // src/core/instance/observer/watcher.js |
触发get的时机
在执行mountComponent
的时候,会实例化一个Watcher
对象(渲染watcher),并把updateComponent
函数传入其中,如果是函数
1 | // Watcher的get方法 |
在Watcher
实例化的过程中,会执行到this.get()
,pushTarget(this)
将当前渲染watcher赋值给Dep.target
并推入栈中,以便之后回复上一个父状态,之后执行之前传入的updateComponent => vm.update(vm._render(), hydrating)
触发了render
,在render的时候就会触发在模板中使用到的数据的getter
,而此时Dep.target
就是之前实例化的渲染watcher
defineReactive
中添加的get:
1 | get: function reactiveGetter () { |
如果dep
subs中没有这个watcher,将这个watcherpush
进去
总结
上节说到,在创建响应式对象的时候,会给每个属性添加__ob__
属性,这个属性就是一个Dep
的实例,dep
对象的作用就是收集了所有该属性的watcher
,之后在set
的时候去触发所有的watcher
来通知改变的发生
派发更新
在修改数据的时候,会触发在defineReactive
中的set
逻辑
1 | set: function reactiveSetter (newVal) { |
notify
方法就是遍历dep
实例subs
属性中存的watcher
对象数组,分别触发他们的update
方法
1 | // watcher.js update方法 |
包括计算属性、同步过程和其它情况,本节应该进入的是queueWatcher
1 | // observer/scheduler.js queueWatcher 方法 |
如果watcher不在全局的队列中,push进去
1 | // watcher run |
渲染watcher派发更新执行过程
数据set=>dep.notify()=>watcher.update()=>渲染watcher:queueWatcher()=>flushSchedulerQueue()=>watcher.run()=>watcher.getAndInvoke()=>watcher.get()=>watcher.get()=>watcher.getter()=>getter = expOrFn(创建watcher实例传入的第二个参数)=>updateComponent()=>vm._update()
总结
- 在对
props
、data
等数据初始化过程中,会调用observe
方法使其中的数据变为响应式对象 observe
方法初始化一个Observer
类的实例,传入需要观测的对象Observer
对象初始化过程中在观测的数据上添加__ob__
属性,并将自身的值赋给__ob__
;再对观测数据是数组的情况特别处理,保证属性是对象的情况也被观测到。最终对对象里的每一个值调用defineReactive
defineReactive
会将传入的对象变成一个响应式对象,添加getter
和setter
- 在
get
中,调用dep
(dep
在defineReactive
函数的闭包中会被保存)的depend
方法,将Dep.target
(当前的watcher对象)保存在subs
属性中,用于之后派发更新触发所有wathcer
对象update
- 在
set
中,调用闭包中dep
对象的notify
方法,循环subs
属性中所有的watcher
,调用update
;在渲染watcher
的update
中,调用queueWatcher
将要更新的watcher
push到一个队列中,在nextTick
的时候调用flushScedulerQueue
flushSchedulerQueue
会对queue
以watcher
的id大小排序,保证渲染顺序,并调用watcher.run()
run
方法又调用getAndInvoke
方法,在渲染watcher
的情况下,调用watcher
实例的get
方法,调用了getter
属性(实例化watcher时传入的第二个参数),在渲染watcher
的情况下就是updateComponent
方法,最终触发vm._update
开始重新渲染