js设计模式之发布-订阅模式

一、简单介绍

发布订阅模式又叫观察者模式,当一个对象的状态发生改变时,依赖于这个状态的对象都将得到通知。

二、应用场景

最常见的发布-订阅模式,就是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);
      });
    }
  }
}

这样,我们就将发布和订阅的功能封装了出来,而我们的业务组件里面就不需要耦合发布订阅的代码。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值