一、觀察者模式簡介
觀察者模式定義了對象間的一種一對多的組合關系,當一個對象的狀態發生變化時,所有依賴於它的對象都得到通知並自動刷新。觀察者模式必須包含兩個角色:觀察者和觀察對象,兩者之間存在“觀察”的邏輯關聯,當觀察對象狀態發生改變時,將通知相應的觀察者以更新狀態。
二、Vue中觀察者模式介紹
項目源碼:https://github.com/vuejs/vue/tree/dev/src/core/observer
下圖為Vue框架在數據初始化中使用觀察者模式的示意圖:
(圖片來源:https://blog.csdn.net/github_36369819/article/details/79201314)
Dep類進行依賴收集,即通過subs數組記錄訂閱者(觀察者)Watcher,當數據狀態發生改變時,通知訂閱者Watcher進行數據更新(update)操作。以下列出了Dep類中部分方法的代碼,詳細代碼見:Dep.js
/** * A dep is an observable that can have multiple * directives subscribing to it. */ export default class Dep { static target: ?Watcher; id: number; subs: Array<Watcher>; constructor () { this.id = uid++ this.subs = [] } addSub (sub: Watcher) { this.subs.push(sub) } removeSub (sub: Watcher) { remove(this.subs, sub) } depend () { if (Dep.target) { Dep.target.addDep(this) } } notify () { // stabilize the subscriber list first const subs = this.subs.slice() if (process.env.NODE_ENV !== 'production' && !config.async) { // subs aren't sorted in scheduler if not running async // we need to sort them now to make sure they fire in correct // order subs.sort((a, b) => a.id - b.id) } for (let i = 0, l = subs.length; i < l; i++) { subs[i].update() } } } // the current target watcher being evaluated. // this is globally unique because there could be only one // watcher being evaluated at any time. Dep.target = null
Watcher類為觀察者,訂閱Dep類,能接受Dep類發出的數據更新通知進行update操作。以下代碼列出了Watcher類中主要的幾個方法:訂閱、更新等,詳細代碼見:Watcher.js
/** * Evaluate the getter, and re-collect dependencies. */ get () { pushTarget(this) let value const vm = this.vm try { value = this.getter.call(vm, vm) } catch (e) { if (this.user) { handleError(e, vm, `getter for watcher "${this.expression}"`) } else { throw e } } finally { // "touch" every property so they are all tracked as // dependencies for deep watching if (this.deep) { traverse(value) } popTarget() this.cleanupDeps() } return value } /** * Add a dependency to this directive. */ addDep (dep: Dep) { const id = dep.id if (!this.newDepIds.has(id)) { this.newDepIds.add(id) this.newDeps.push(dep) if (!this.depIds.has(id)) { dep.addSub(this) } } } /** * Clean up for dependency collection. */ cleanupDeps () { let i = this.deps.length while (i--) { const dep = this.deps[i] if (!this.newDepIds.has(dep.id)) { dep.removeSub(this) } } let tmp = this.depIds this.depIds = this.newDepIds this.newDepIds = tmp this.newDepIds.clear() tmp = this.deps this.deps = this.newDeps this.newDeps = tmp this.newDeps.length = 0 } /** * Subscriber interface. * Will be called when a dependency changes. */ update () { /* istanbul ignore else */ if (this.lazy) { this.dirty = true } else if (this.sync) { this.run() } else { queueWatcher(this) } }
三、觀察者模式的優缺點
被觀察對象和觀察者之間是抽象耦合,且耦合程度很低,有助於擴展與重用;能進行簡單的廣播通信,自動通知所有訂閱的觀察者;觀察者並不知道其他觀察者的存在,若直接對被觀察目標操作,造成一系列的更新,可能產生意外情況。