《前端设计模式魔法手册》-观察者模式(Observer Pattern)——前端组件通信的「广播电台」

观察者模式(Observer Pattern)------前端组件通信的「广播电台」

本文是《前端设计模式魔法手册》系列第 1 篇,完整代码示例可在 GitHub仓库 获取。

一、模式简介

1.1 什么是观察者模式?

观察者模式 是一种行为设计模式,它定义了一种 一对多 的依赖关系:

  • 当一个对象(称为 Subject,主题/被观察者)的状态发生改变时
  • 所有依赖它的对象(称为 Observers,观察者)都会自动收到通知并更新

1.2 类比现实生活

  • 📻 广播电台(Subject):只管发射信号,不关心谁在收听
  • 🎧 听众(Observers):随时可以切换频道或关闭收音机
  • 🔄 松耦合:电台和听众互不干扰,仅通过信号频率联系

二、前端为何需要观察者模式?

2.1 解决痛点

问题场景 观察者模式的解决方案
组件A需要通知组件B,但两者没有直接引用关系 通过中间Subject通信,避免硬编码依赖
多处UI需要同步同一份数据(如用户登录状态) 状态变更时自动批量通知所有观察者
异步事件(如WebSocket推送)需要触发多个处理逻辑 观察者各自订阅事件,避免回调地狱

2.2 典型应用场景

  • 🔔 实时数据更新:股票价格、聊天消息、通知中心
  • 🎛️ UI组件协同:表单验证、步骤向导、多标签页同步
  • 🛠️ 框架核心机制:Vue响应式、Redux状态订阅、React Context

三、模式实现

3.1 基础实现(TypeScript)

typescript 复制代码
// 主题接口
interface Subject {
  addObserver(observer: Observer): void;
  removeObserver(observer: Observer): void;
  notify(): void;
}

// 观察者接口
interface Observer {
  update(data: any): void;
}

// 具体主题:消息通知中心
class MessageNotifier implements Subject {
  private observers: Observer[] = [];
  private messageCount: number = 0;

  addObserver(observer: Observer) {
    this.observers.push(observer);
  }

  removeObserver(observer: Observer) {
    this.observers = this.observers.filter(obs => obs !== observer);
  }

  notify() {
    this.observers.forEach(observer => 
      observer.update(this.messageCount)
    );
  }

  // 业务方法
  setMessageCount(count: number) {
    this.messageCount = count;
    this.notify(); // 关键点:状态变化时自动通知
  }
}

// 具体观察者:导航栏
class NavBar implements Observer {
  update(count: number) {
    console.log(`NavBar收到消息数: ${count}`);
    // 实际开发中更新DOM或组件状态
  }
}

3.2 使用示例

typescript 复制代码
const notifier = new MessageNotifier();
const navBar = new NavBar();
const sidebar = new Sidebar(); // 假设已实现

// 订阅
notifier.addObserver(navBar);
notifier.addObserver(sidebar);

// 触发更新
notifier.setMessageCount(5); 
// 输出:
// NavBar收到消息数: 5
// Sidebar收到消息数: 5

四、前端实战案例

4.1 自定义事件总线(Event Bus)

typescript 复制代码
class EventBus {
  private events: Record<string, Function[]> = {};

  on(event: string, callback: Function) {
    if (!this.events[event]) this.events[event] = [];
    this.events[event].push(callback);
  }

  emit(event: string, payload?: any) {
    this.events[event]?.forEach(cb => cb(payload));
  }
}

// 使用
const bus = new EventBus();
bus.on('login', (user) => {
  console.log(`头部导航更新用户: ${user.name}`);
});
bus.emit('login', { name: 'Alice' });

4.2 Vue响应式原理简化版

javascript 复制代码
// 模拟Vue的Dep和Watcher机制
class Dep {
  subscribers = new Set();
  depend() { /* 收集依赖 */ }
  notify() { /* 触发更新 */ }
}

// 数据劫持
function defineReactive(obj, key) {
  const dep = new Dep();
  let value = obj[key];
  
  Object.defineProperty(obj, key, {
    get() {
      dep.depend(); // 当前正在执行的Watcher会被收集
      return value;
    },
    set(newVal) {
      value = newVal;
      dep.notify(); // 通知所有Watcher更新
    }
  });
}

五、优劣分析

✅ 优点

  • 解耦:发布者与订阅者无需相互引用
  • 动态关系:运行时可以随时添加/移除观察者
  • 广播通信:一次变更通知多个对象

⚠️ 注意事项

  • 性能问题:观察者过多时,通知遍历可能耗时
  • 内存泄漏:忘记取消订阅会导致对象无法回收
  • 调试困难:通知链路复杂时难以追踪调用栈

六、扩展知识

6.1 观察者 vs 发布-订阅

观察者模式:

graph TD Subject --直接通知--> Observer

发布订阅模式:

graph TD Publisher --通过Broker--> Subscriber

6.2 浏览器内置观察者API

API 用途
MutationObserver 监听DOM变化
IntersectionObserver 监听元素可见性
ResizeObserver 监听元素尺寸变化
PerformanceObserver 监听性能指标

下一篇预告

《装饰器模式:如何给前端组件动态「加Buff」?》

讨论题

在你们项目中,哪些场景可以用观察者模式优化?欢迎在评论区分享案例!

相关推荐
二十雨辰12 分钟前
[HTML5]快速掌握canvas
前端·html
tingkeiii43 分钟前
【react+antd+vite】优雅的引入svg和阿里巴巴图标
前端·react.js·前端框架
清幽竹客1 小时前
vue-18(使用 Vuex 插件实现高级功能)
前端·vue.js·前端框架·vue
粥里有勺糖1 小时前
用Trae做了个公众号小工具
前端·ai编程·trae
棉花糖超人2 小时前
【从0-1的HTML】第2篇:HTML标签
前端·html
shepherd1112 小时前
一文带你从入门到实战全面掌握RocketMQ核心概念、架构部署、实践应用和高级特性
架构·消息队列·rocketmq
exploration-earth2 小时前
本地优先的状态管理与工具选型策略
开发语言·前端·javascript
season_zhu2 小时前
iOS开发:关于日志框架
ios·架构·swift
小马爱记录2 小时前
Sentinel微服务保护
spring cloud·微服务·架构·sentinel
OpenTiny社区2 小时前
开源之夏报名倒计时3天!还有9个前端任务有余位,快来申请吧~
前端·github