一、观察者模式简介
观察者模式定义了对象间的一种一对多的组合关系,当一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新。观察者模式必须包含两个角色:观察者和观察对象,两者之间存在“观察”的逻辑关联,当观察对象状态发生改变时,将通知相应的观察者以更新状态。
二、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) } }
三、观察者模式的优缺点
被观察对象和观察者之间是抽象耦合,且耦合程度很低,有助于扩展与重用;能进行简单的广播通信,自动通知所有订阅的观察者;观察者并不知道其他观察者的存在,若直接对被观察目标操作,造成一系列的更新,可能产生意外情况。