最近又回過頭來看 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
方法並加入異步更新隊列,這就是觸發依賴過程。