观察者模式的主要实现是通过发布者与订阅者来实现消息的便捷传递。
在Vue源码中,我观察到主要由以下三个步骤来实现:
1.对每个对象进行数据绑定
1 1 function defineReactive ( 2 2 obj, 3 3 key, 4 4 val, 5 5 customSetter, 6 6 shallow 7 7 ) { 8 8 //新建一个发布者对象进行注册 9 9 var dep = new Dep(); 10 10 //获取属性进行绑定 11 11 var property = Object.getOwnPropertyDescriptor(obj, key); 12 12 if (property && property.configurable === false) { 13 13 return 14 14 } 15 15 16 16 // 适应预先定义的get set方法 17 17 var getter = property && property.get; 18 18 var setter = property && property.set; 19 19 if ((!getter || setter) && arguments.length === 2) { 20 20 val = obj[key]; 21 21 } 22 22 23 23 var childOb = !shallow && observe(val); 24 24 Object.defineProperty(obj, key, { 25 25 enumerable: true, 26 26 configurable: true, 27 27 //对getter方法进行劫持 28 28 get: function reactiveGetter () { 29 29 var value = getter ? getter.call(obj) : val; 30 30 if (Dep.target) { 31 31 dep.depend(); 32 32 if (childOb) { 33 33 childOb.dep.depend(); 34 34 if (Array.isArray(value)) { 35 35 dependArray(value); 36 36 } 37 37 } 38 38 } 39 39 return value 40 40 }, 41 41 //数据变更后才能察觉到 42 42 set: function reactiveSetter (newVal) { 43 43 var value = getter ? getter.call(obj) : val; 44 44 /* eslint-disable no-self-compare */ 45 45 if (newVal === value || (newVal !== newVal && value !== value)) { 46 46 return 47 47 } 48 48 /* eslint-enable no-self-compare */ 49 49 if (process.env.NODE_ENV !== 'production' && customSetter) { 50 50 customSetter(); 51 51 } 52 52 if (setter) { 53 53 setter.call(obj, newVal); 54 54 } else { 55 55 val = newVal; 56 56 } 57 57 childOb = !shallow && observe(newVal); 58 58 //发布者进行通知 59 59 dep.notify(); 60 60 } 61 61 }); 62 62 }
2.发布者
Dep :作用主要有两个,① 添加移除订阅者 ② 发布消息到每一个订阅者
1 export default class Dep { 2 static target: ?Watcher; 3 id: number; 4 subs: Array<Watcher>; 5 6 constructor () { 7 //为每个观察者分配一个id作为标示 8 this.id = uid++ 9 this.subs = [] 10 } 11 //添加观察者 12 addSub (sub: Watcher) { 13 this.subs.push(sub) 14 } 15 //移除观察者 16 removeSub (sub: Watcher) { 17 remove(this.subs, sub) 18 } 19 20 depend () { 21 if (Dep.target) { 22 Dep.target.addDep(this) 23 } 24 } 25 26 notify () { 27 // 首先对观察者列表进行处理 28 const subs = this.subs.slice() 29 if (process.env.NODE_ENV !== 'production' && !config.async) { 30 // subs aren't sorted in scheduler if not running async 31 // we need to sort them now to make sure they fire in correct 32 // order 33 subs.sort((a, b) => a.id - b.id) 34 } 35 //把消息的变更通知到每一个订阅者 36 for (let i = 0, l = subs.length; i < l; i++) { 37 subs[i].update() 38 } 39 } 40 } 41 42 // the current target watcher being evaluated. 43 // this is globally unique because there could be only one 44 // watcher being evaluated at any time. 45 Dep.target = null 46 const targetStack = [] 47 48 export function pushTarget (_target: ?Watcher) { 49 if (Dep.target) targetStack.push(Dep.target) 50 Dep.target = _target 51 } 52 53 export function popTarget () { 54 Dep.target = targetStack.pop() 55 }
3.观察者
Watcher 主要作用:① 订阅Dep ② 收到notify通知时,进行update操作
1 export default class Watcher { 2 vm: Component; 3 expression: string; 4 cb: Function; 5 6 constructor ( 7 vm: Component, 8 expOrFn: string | Function, 9 cb: Function, 10 options?: Object 11 ) { 12 this.vm = vm 13 vm._watchers.push(this) 14 this.cb = cb 15 // parse expression for getter 16 if (typeof expOrFn === 'function') { 17 this.getter = expOrFn 18 } else { 19 // 解析表达式 20 this.getter = parsePath(expOrFn) 21 if (!this.getter) { 22 this.getter = function () {} 23 } 24 } 25 this.value = this.get() 26 } 27 28 get () { 29 // 将目标收集到目标栈 30 pushTarget(this) 31 const vm = this.vm 32 33 let value = this.getter.call(vm, vm) 34 // 删除目标 35 popTarget() 36 37 return value 38 } 39 40 // 订阅 Dep,同时让 Dep 知道自己订阅着它 41 addDep (dep: Dep) { 42 const id = dep.id 43 if (!this.newDepIds.has(id)) { 44 this.newDepIds.add(id) 45 this.newDeps.push(dep) 46 if (!this.depIds.has(id)) { 47 // 收集订阅者 48 dep.addSub(this) 49 } 50 } 51 } 52 53 // 订阅者'消费'动作,当接收到变更时则会执行 54 update () { 55 this.run() 56 } 57 58 run () { 59 const value = this.get() 60 const oldValue = this.value 61 this.value = value 62 this.cb.call(this.vm, value, oldValue) 63 } 64 }
总结:
本次对vue源码的阅读,让我对观察者模式有了进一步的认识,目的是为了实现一对多依赖关系的解耦。在发布者这本身状态改变时能够发布通知使得每个订阅者能够自己去做出相应的改变。