发布—订阅模式 又叫观察者模式。
发布订阅模式的优点:
1.广泛应用于异步编程中,这是一种替代传递回调函数的方案。在异步编程中使用发布订阅模式,我们就无需过多关注对象在异步运行期间的内部状态,而只需要订阅感兴趣的事件发生点。例如ZoomSdk的注册以及事件回调都是异步执行的,这时使用发布订阅模式就很好解决这个问题。
2.发布订阅模式可以取代对象之间硬编码的通知机制,一个对象不再显式地调用另一个对象的某个接口。发布订阅模式让两个对象松耦合地联系在一起,虽然不太清除批次的细节,但这不影响他们之间互相通讯。当有心得订阅者出现时,发布者的代码不需要任何修改;同样发布者需要改变时,也不会影响到之前的订阅者。只要之前约定的事件名称没有变化,就可以自由地改变他们。
例子1:DOM 节点绑定事件函数
document.addEventListener('clikc', function(){ // 订阅者,监听click事件,而且不知道发布者什么时候才会触发click事件
alert('have clicked');
}, false)
document.body.click(); // 发布者,发布click事件在合适的位置。
例子2:自定义事件
// utils/event.js file定义了发布订阅对象。
let Event = (function(){
let clientList = {};
let listten = function(key, fn){
if(!clientList[key]){
clientList[key] = [];
}
clientList[key].push(fn);
}
let tigger = function(){
let key = Array.prototype.shift.call(arguments),
let fns = clientList[key];
if(!fns || fns.length === 0){
return false;
}
for(let i = 0; fn; fn = fns[i++];){
fn.apply(this,arguments);
}
}
let remove = function(key, fn){
let fns = clientList[key];
if (!fns){
return false;
}
if (!fn){
fns && (fns.length = 0);
}else{
for (let i=fns.length -1; i >=0; i--){
let _fn = fns[i];
if (_fn === fn){
fns.splice(i, 1);
}
}
}
}
return {
listen: listen,
trigger:trigger,
remove:remove
}
})();
// 其他home.js files引入event.js并且使用添加订阅者或者添加发布者
import Event from '@/utils/event.js'
...
Event.listen('loginSuccess', function(info){
router.push('/home');
})
...
// loginAction.js中请求成功后发布消息订阅
import Event from '@/utils/event.js'
...
request.('login').then(res => {
Event.trigger('loginSuccess', '') // 发布消息
}).catch(err => {
Event.trigger('loginError', '') // 发布消息
})
....
发布订阅的优点很明显,但也有缺点。创建订阅者本身要消耗一定的时间和内存,而且当订阅一个消息后,也许此消息最后都没有发生,但这个订阅者会始终存在于内存中。另外,发布订阅模式虽然可以弱化对象之间的联系,但如果过度使用的话,对象和对象之间的必然联系也被深埋在背后,会导致程序难以跟踪维护和理解。特别是有多个发布者和订阅者嵌套到一起的时候,要跟踪一个bug不是一件轻松的事情。
因此双向绑定的底层原理就是应用了发布订阅者模式结合数据劫持,通过Object.defineProperty()方法来劫持各个属性(需要数据绑定的属性,vue 双花括号里面的值就是需要双向数据绑定的属性,如name,