我们知道Vue的响应式原理用到了发布-订阅模式,之前一直分不清观察者模式和发布-订阅模式之前的区别。
观察者模式和发布订阅模式最大的区别就是发布订阅模式有个事件调度中心。

观察者模式
一个目标对象管理所有依赖于它的观察者对象,并且在它本身的状态改变时主要发出通知。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| function Subject(){ this.observers = [] }
Subject.prototype = { constructor: Subject, // 注册 add: function(observer){ this.observers.push(observer) }, notify: function(context){ this.observers.forEach(observer=>{ observer.update(context) }) },
remove: function(observer){ this.observers.splice(this.observers.indexOf(observer),1) }
}
function ObserverA(){ this.update = function(context){ console.log("A update:" + context) } }
function ObserverB(){ this.update = function(context){ console.log("B update:" + context) } }
var a = new ObserverA() var b = new ObserverB()
var subject = new Subject()
subject.add(a) subject.add(b)
subject.notify("data change")
|
发布订阅模式
发布订阅有两个参与者:发布者和订阅者。发布者向某个信道发送一条消息,订阅者绑定这个信道,当有消息发布至信道就会接收到一个通知。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| class PubSub { constructor(){ this.subscribers = {} } subscribe(type, fn){ if(!Object.prototype.hasOwnProperty.call(this.subscribers, type)){ this.subscribers[type] = [] } this.subscribers[type].push(fn) } publish(type, ...args){ let listeners = this.subscribers[type] if(!listeners || !listeners.length) return;
listeners.forEach(fn=>{ fn(...args) }) }
unsubscribe(type, fn){ let listeners = this.subscribers[type] if(!listeners || !listeners.length) return;
this.this.subscribers[type] = listeners.filter(v => v !== fn ) } }
let ob = new PubSub()
ob.subscribe('add',(val) => {console.log(val)})
ob.publish('add', 1)
|
可以看出发布订阅是完全解耦的;而在观察者模式中,目标对象管理观察者对象,双方是耦合的。
Vue 响应式系统
vue的数据初始化
1 2 3 4 5 6 7
| var v = new Vue({ data(){ return { a: 'hello' } } })
|
官网上的图:

图源自@xuqiang521

数据劫持
从上图可以看到,数据劫持的核心方法使用的就是Object.defineProperty()把属性转换为getter/setter。在数据变更的时候,会进入到封装的Dep和Watch中进行处理。
遍历劫持
基本类型和对象的处理
1 2 3 4 5 6
| walk(obj){ const keys = Object.keys() for(let i = 0;i < keys.length; i++){ defineReactive(obj, keys[i], obj[keys[i]]) } }
|
核心的劫持相关函数已经属性的订阅和发布
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| export default defineReactive( obj: object, key: string, val: any, customSetter?: Function ){ const dep = new Dep() const property = Object.getOwnPropertyDescriptor(obj, key) if (property && property.configurable === false) { return }
const getter = property && property.get const setter = property && property.set
let childOb = observe(val) Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter(){ var value = getter ? getter.call(obj) : val
if(Dep.target){ dep.depend() if(childOb){ childOb.dep.depend() } if(Array.isArray(value)){ dependArray(value) } } return value
}, set: function reactiveSetter(newVal){ const value = getter ? getter.call(obj) : val if(value === newVal || (newVal !== newVal && value !== value)){ return } if (process.env.NODE_ENV !== 'production' && customSetter) { customSetter() } if(setter){ setter.call(obj, newVal) } else { val = newVal }
childOb = observe(newVal)
dep.notify() } })
}
|
###
