最近又回过头来看 vue 的响应式原理,发现越看越迷了,Observer
、Dep
、Watcher
三者的做用是什么,他们的的关系到底是怎样的呢?javascript
我以为搞清楚这些,首先要知道 vue 初始化的过程。咱们从 new Vue()
开始,构造函数会执行 this._init
,在 _init
中会进行合并配置、初始化生命周期、事件、渲染等,最后执行 vm.$mount
进行挂载。vue
// src/core/instance/index.js
function Vue (options) {
// ...
this._init(options)
}
// src/core/instance/init.js
Vue.prototype._init = function (options?: Object) {
// 合并配置
// ...
// 一系列初始化
// ...
initState(vm)
// ...
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
复制代码
这里主要来看 initState(vm)
,响应式的核心均在于此,会进行 props
、data
、computed
、watch
的初始化操做。java
// src/core/instance/state.js
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props) // 初始化 props
// ...
if (opts.data) {
initData(vm) // 初始化 data
} else {
observe(vm._data = {}, true /* asRootData */)
}
// ...
}
复制代码
了解了初始化过程,接下来就引出 Observer
。react
在上面的 initData
中会执行 observe
方法进而实例化 Observer
。Observer
的实例化过程就是递归地把 data 对象和子对象添加 __ob__
属性同时经过咱们熟知的 defindReactive
为属性定义 getter/setter
。数组
那么 Observer
顾名思义是观察者,观察的就是 data,它经过数据劫持使 data 的读写都处于它的监管之下。那么在观察到数据发生变化时会作出怎样的操做呢?闭包
来到 defindReactive
的核心代码,会看到 getter
里的 dep.depend()
和 setter
里的 dep.notify()
,这就是依赖收集和触发更新的起点。这里的 dep
是 defindReactive
内定义的一个常量,getter/setter
函数内持有对它的闭包引用,Dep
就是引出的下一个概念。异步
// src/core/observer/index.js
// ...
const dep = new Dep()
// ...
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
// ...
dep.depend()
//...
},
set: function reactiveSetter (newVal) {
// ...
dep.notify()
},
复制代码
先看下 Dep
类的定义函数
// src/core/observer/dep.js
export default class Dep {
constructor () {
this.id = uid++
this.subs = []
}
addSub (sub: Watcher) {}
removeSub (sub: Watcher) {}
depend () {}
notify () {}
}
复制代码
能够看到 Dep
有一个实例属性 subs 数组,实例方法 addSub/removeSub
添加和删除数组中的某项。由此能够肯定 dep
实例并不是依赖而是依赖的管理者,subs
数组即为依赖(订阅者)列表,它们就是接下来登场的 Watcher
oop
从上面知道 Observer
Dep
都已经在 initState
中实例化了,响应式数据和依赖管理都准备好了,接下来就须要 Wacther 来订阅了。那么 Wacther 何时实例化呢,回到开头的初始化过程最后 vm.$mount
挂载,在这以后会执行 mountComponent
方法,Watcher
就是在这里实例化的(暂不关注 computed watcher 和 watch 选项的 watcher)。ui
// src/core/instance/lifecycle.js
export function mountComponent () {
// ...
new Watcher(vm, updateComponent, noop, {
// ...
}, true /* isRenderWatcher */)
}
复制代码
Watcher
的定义以下,实例化时会执行 get
方法对传入的 updateComponent
进行求值,updateComponent
也就是 _render
函数,执行 _render
函数会读取 data 数据从而触发 getter
进行依赖收集。
// src/core/observer/watcher.js
export default class Watcher {
// 对 getter 求值,进行依赖收集
get () {}
// 触发更新
update() {}
}
复制代码
至此,咱们能够总结一下三者的关系:
Observer
将数据定义为响应式,每一个 Observer
实例都有本身的 Dep
来管理依赖。实例化 Wacther
的时候进行求值会触发 getter
,进而执行 dep.depend()
将当前 Wacther
加入 Dep 维护的依赖列表,这就是依赖收集过程。setter
执行 dep.notify
,Dep
会执行全部依赖的 update
方法并加入异步更新队列,这就是触发依赖过程。