前端框架 MVVM 实现原理之—— 发布/订阅模式

在使用 VUE 框架实现 AOS 系统的重构,因此学习了 MVVM 的实现原理是数据劫持+发布订阅模式,这篇记录下 JavaScript 实现的发布/订阅模式。

一、什么是发布/订阅模式
发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知。
好处:
  • 支持简单的广播通信,自动通知所有已经订阅过的对象
  • 页面载入后目标对象很容易与观察者存在一种动态关联,增加了灵活性
  • 目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用

二、实现发布/订阅模式
  1. 指定一个发布者
  2. 给发布者添加一个缓冲列表,用于存放回调函数以用于通知订阅者
  3. 发布消息时,发布者遍历缓存列表,依次触发每个订阅者的回调函数
// 简单的天气订阅
var Weather = {
list: [], // 缓存列表
listen: function(fn) { // 增加订阅者
this.list.push(fn)
},
publish: function() { // 发布消息
for(var i=0,fn; fn=this.list[i++];) {
fn.apply(this,arguments);
}
}
};

// 订阅消息
Weather.listen(function(weather, wind){
console.log('天气:' + weather, '风力:'+ wind);
})

// 发布消息
Weather.publish("晴天","微风"); // 天气:晴天 风力:微风
Weather.publish("雷阵雨","5级风"); // 天气:雷阵雨 风力:5级风​
运行结果:


三、自定义发布/订阅模式

思路:
  1. 创建一个对象(缓存列表)
  2. addlisten 方法用来把订阅回调函数 fn 都加到缓存列表 listenList 中
  3. publish 方法取到 arguments 里第一个当做key,根据 key 值去执行对应缓存列表中的函数
  4. remove 方法可以根据 key 值取消订阅

// 升级版天气订阅
var Weather = {
listenList:{}, //缓存对象
// 增加订阅者
addlisten:function(key,fn){
// 没有没有key给个初值避免调用报错
if (!this.listenList[key]) {
this.listenList[key] = [];
}
// 增加订阅者,一个key就是一种订阅类型
this.listenList[key].push(fn);
},
publish:function(){
const key = Array.from(arguments).shift()
const fns = this.listenList[key]

// 这里排除两种特殊情况,第一种为触发的一种从未订阅的类型,第二种订阅后取消了所有订阅的
if(!fns || fns.length===0){
return false;
}

// 发布消息,触发同类型的所有订阅,
fns.forEach((fn)=>{
fn.apply(this,arguments);
})
},
remove:function(key,fn){
var fns=this.listenList[key];//取出该类型的对应的消息集合
if(!fns){//如果对应的key没有订阅直接返回
return false;
}
if(!fn){//如果没有传入具体的回掉,则表示需要取消所有订阅
fns && (fns.length=0);
}else{
for(var l=fns.length-1;l>=0;l--){//遍历回掉函数列表
if(fn===fns[l]){ // 这里是传入地址的比较,所以不能直接用匿名函数了
fns.splice(l,1);//删除订阅者的回掉
}
}
}
}
};
// 台风
function taifeng(weather, wind){
console.log("天气:"+ weather +",风力:"+ wind);
}
// 晴天
function qingtian(weather, wind){
console.log("天气:"+ weather +",风力:"+ wind);
}
// 雷阵雨
function leizhenyu(weather, wind){
console.log("天气:"+ weather +",风力:"+ wind);
}

// 订阅了台风通知
Weather.addlisten("台风", taifeng);
// 订阅了晴天通知
Weather.addlisten("晴天", qingtian);
// 订阅了雷阵雨通知
Weather.addlisten("雷阵雨", leizhenyu);

// 取消订阅晴天通知
Weather.remove("晴天", qingtian);

// 发布订阅
Weather.publish("台风","12级");

Weather.publish("晴天","1级");

Weather.publish("雷阵雨","2级");​

运行结果:



缺点:
       1.状态未知

               发布者不知道订阅者的状态,反之亦然,这样的话,你根本不知道在另一端是否会没有问题?
       2.不能识别恶意消息

               攻击者(恶意的发布者)能够入侵系统并且撕开它。这会导致恶意的消息被发布,订阅者能够获得他们以前并不能获得的消息。
       3.关系更新难

                更新发布者和订阅者的关系会是一个很难的问题,因为毕竟他们根本不认识对方

参考:
https://segmentfault.com/a/1190000020886430
https://zhaomenghuan.js.org/note/javascript/javascript-pub-sub.html


★博文内容均由个人提供,与平台无关,如有违法或侵权,请与网站管理员联系。

★文明上网,请理性发言。内容一周内被举报5次,发文人进小黑屋喔~

评论