WHAT - 发布订阅

在前端开发中,发布订阅是一种常见的开发场景,允许一个对象(发布者)发布事件,而多个对象(订阅者)可以订阅并接收这些事件。

发布订阅在设计模式中,可以理解为 观察者模式 / Observer Pattern

一、常见实现方案

以下是一些常见的实现方案:

1.1 使用事件发射器(Event Emitter)

许多 JavaScript 框架和库内置了事件发射器机制,例如 Node.js 的 EventEmitter 类。

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();

// 定义事件
eventEmitter.on('event', (data) => {
  console.log('Event received:', data);
});

// 触发事件
eventEmitter.emit('event', 'Hello World!');

1.2 自定义事件系统(EventBus)

自己实现一个简单的发布订阅系统,可以通过维护一个事件监听器的映射表来实现。

class EventBus {
  constructor() {
    this.listeners = {};
  }

  on(event, listener) {
    if (!this.listeners[event]) {
      this.listeners[event] = [];
    }
    this.listeners[event].push(listener);
  }

  emit(event, data) {
    if (this.listeners[event]) {
      this.listeners[event].forEach(listener => listener(data));
    }
  }

  off(event, listener) {
    if (this.listeners[event]) {
      this.listeners[event] = this.listeners[event].filter(l => l !== listener);
    }
  }
}

const eventBus = new EventBus();
eventBus.on('message', (data) => console.log('Message received:', data));
eventBus.emit('message', 'Hello EventBus!');

1.3 使用库如 PubSubJS

PubSubJS 是一个轻量级的 JavaScript 发布订阅库。

const PubSub = require('pubsub-js');

// 订阅
const token = PubSub.subscribe('TOPIC', (msg, data) => {
  console.log(msg, data);
});

// 发布
PubSub.publish('TOPIC', 'Hello PubSubJS!');

// 取消订阅
PubSub.unsubscribe(token);

1.4 使用框架内置的状态管理工具

许多现代前端框架如 Vue.js、React 和 Angular 提供了内置的状态管理工具,可以用来实现发布订阅模式。例如:

Vue.js

const EventBus = new Vue();

// 组件A:发布事件
EventBus.$emit('myEvent', 'Hello from Component A');

// 组件B:订阅事件
EventBus.$on('myEvent', (data) => {
  console.log(data);
});

React (使用 Context API 或 Redux)

// 使用 Context API
const MyContext = React.createContext();

// 提供者组件
const MyProvider = ({ children }) => {
  const [state, setState] = useState(null);

  const publish = (data) => {
    setState(data);
  };

  return (
    <MyContext.Provider value={{ state, publish }}>
      {children}
    </MyContext.Provider>
  );
};

// 订阅者组件
const MySubscriber = () => {
  const { state, publish } = useContext(MyContext);
  
  useEffect(() => {
    console.log('State updated:', state);
  }, [state]);

二、先后关系

可以先订阅后发布,那可以先发布后订阅吗?

在发布订阅模式中,先订阅后发布是非常常见的做法,因为这通常是实现实时事件通知的基本方式:订阅者先准备好接收消息,然后发布者发送消息

然而,某些情况下,也可能需要在没有订阅者存在的情况下发布消息,并且在订阅者稍后订阅时能够收到之前发布的消息。要实现这种“先发布后订阅”的机制,可以采用以下几种方法:

2.1 缓存事件数据

发布者在发布消息时,将消息暂时存储在一个缓存中,当新的订阅者订阅时,可以将缓存中的消息发送给订阅者。

class EventBus {
  constructor() {
    this.listeners = {};
    this.cachedEvents = {};
  }

  on(event, listener) {
    if (!this.listeners[event]) {
      this.listeners[event] = [];
    }
    this.listeners[event].push(listener);
    
    // 如果有缓存的事件,立即触发
    if (this.cachedEvents[event]) {
      listener(this.cachedEvents[event]);
    }
  }

  emit(event, data) {
    if (this.listeners[event]) {
      this.listeners[event].forEach(listener => listener(data));
    }
    // 缓存事件数据
    this.cachedEvents[event] = data;
  }

  off(event, listener) {
    if (this.listeners[event]) {
      this.listeners[event] = this.listeners[event].filter(l => l !== listener);
    }
  }
}

const eventBus = new EventBus();

// 发布事件
eventBus.emit('message', 'This is a cached message');

// 订阅事件
eventBus.on('message', (data) => {
  console.log('Message received:', data); // Output: 'This is a cached message'
});

2.2 使用 Redux 或 Vuex 等状态管理工具

在前端框架中使用状态管理工具,例如 Redux(React)或 Vuex(Vue.js),可以在状态发生变化时订阅并触发相应的处理逻辑。状态管理工具的状态是持久的,订阅者在任何时候都可以获取当前的状态。

Redux 示例

const { createStore } = require('redux');

// 定义 action 类型
const SET_MESSAGE = 'SET_MESSAGE';

// 定义 action 创建函数
const setMessage = (message) => ({
  type: SET_MESSAGE,
  payload: message
});

// 定义 reducer
const messageReducer = (state = null, action) => {
  switch (action.type) {
    case SET_MESSAGE:
      return action.payload;
    default:
      return state;
  }
};

// 创建 Redux store
const store = createStore(messageReducer);

// 订阅 store
const unsubscribe = store.subscribe(() => {
  const state = store.getState();
  console.log('State updated:', state);
});

// 发布 action
store.dispatch(setMessage('This is a Redux message'));

2.3 使用本地存储或 IndexedDB

如果需要跨页面持久化数据,可以使用浏览器的本地存储(LocalStorage)或 IndexedDB。发布者将消息存储到本地存储中,订阅者在订阅时从本地存储中读取数据。

// 发布消息
localStorage.setItem('message', 'This is a persisted message');

// 订阅消息
const cachedMessage = localStorage.getItem('message');
if (cachedMessage) {
  console.log('Message received:', cachedMessage);
}

通过这些方法,可以实现“先发布后订阅”的功能,确保订阅者能够收到之前发布的消息。选择哪种方法可以根据具体需求和技术栈来决定。

相关推荐

  1. WHAT - 发布订阅

    2024-06-09 00:18:03       10 阅读
  2. Redis-发布订阅

    2024-06-09 00:18:03       20 阅读
  3. Redis 怎么发布订阅

    2024-06-09 00:18:03       16 阅读
  4. Redis 订阅发布(Pub/Sub) 详解 如何使用订阅发布

    2024-06-09 00:18:03       19 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-09 00:18:03       19 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-09 00:18:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-09 00:18:03       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-09 00:18:03       20 阅读

热门阅读

  1. Chrome DevTools攻略:提升开发效率的利器

    2024-06-09 00:18:03       10 阅读
  2. Vue2快速上手

    2024-06-09 00:18:03       10 阅读
  3. android room数据库升级脚本常见问题

    2024-06-09 00:18:03       8 阅读
  4. Hive 面试题(六)

    2024-06-09 00:18:03       13 阅读
  5. stm32编译原理

    2024-06-09 00:18:03       7 阅读
  6. 评价GPT-4的方案

    2024-06-09 00:18:03       10 阅读
  7. ARM功耗管理之功耗状态及功耗模式

    2024-06-09 00:18:03       9 阅读