一、简单介绍
发布订阅模式又叫观察者模式,当一个对象的状态发生改变时,依赖于这个状态的对象都将得到通知。
二、应用场景
最常见的发布-订阅模式,就是dom的事件监听。我们给dom元素绑定一个事件(如click事件),当对应的交互触发时,我们绑定的事件就会被触发。另外还有目前常用的vue框架,它实现响应式的基础就是发布订阅模式。
三、示例
现在很多页面都有图片按需加载的需求,即页面滚动到对应的区域时,才加载我们的图片或者其他资源。我们可以将加载图片封装成一个方法,然后将每个图片的方法注册到页面所在的组件里。在父组件的滚动事件中判断哪些图片已经处于可视区域,然后执行对应图片的加载事件。当然,因为图片加载是一次性的,所以执行了一次之后不需要再次触发了。(需不需要重复触发看使用场景)。以下是简单的demo。
class LoadImage {
//ele :dom元素 src:实际的路径 defaultsrc:占位图的路径 y:距离页面顶端的距离
constructor(ele, src, defaultsrc, x, y) {
this.ele = ele;
this.src = src;
this.ele.src = defaultsrc;
this.y = y;
this.id = Math.random();
}
load() {
const img = new Image();
img.src = this.src;
img.onload = () => {
this.ele.src = this.src;
};
}
loadimg(params) {
this.load();
//do anything
}
}
class Page {
constructor(ele) {
this.ele = ele;
this.imgList = [];
this.init();
}
//注册滚动事件
init() {
this.ele.addEventListener("scroll", this.scrollPage);
}
//判断哪些图片需要加载
scrollPage() {
const currentY = this.ele.clientHeight + this.ele.scrollTop;
const imgList = this.imgList.filter(item => {
return item.y <= currentY;
});
this.load(imgList);
}
//通知图片加载
load(list) {
list.forEach(item => {
item.loadimg("complete");
});
list.forEach(item => {
this.unbind(item.id);
});
}
//注册事件
componentEvent(img) {
this.imgList.push(img);
}
//注销事件
unbind(id) {
const index = this.imgList.findIndex(item => item.id === id);
this.imgList.splice(index, 1);
}
}
const imgList = new Array(5).fill("").map(item => new LoadImage());
const page = new Page();
//注册事件
page.componentEvent(...imgList);
大体思路就是这样。先在发布的对象注册事件,然后发布对象在对应的时机通知订阅的对象执行相应的方法。
四、拓展
当然,这是简单的示例。实际开发中,我们可以将发布-订阅的功能封装为独立的一个对象,用这个对象集中进行订阅、发布的功能。
大体如下:
class EventCenter {
constructor() {
this.event = {};
}
listener(eventHandle, eventName) {
if (this.event[eventName]) {
this.event[eventName].push(eventHandle);
} else {
this.event[eventName] = [eventHandle];
}
}
trigger(eventName, ...params) {
if (this.event[eventName]) {
this.event[eventName].forEach(item => {
item(params);
});
}
}
}
这样,我们就将发布和订阅的功能封装了出来,而我们的业务组件里面就不需要耦合发布订阅的代码。