《前端设计模式魔法手册》-观察者模式(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」?》

讨论题

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

相关推荐
ywf12151 小时前
前端的dist包放到后端springboot项目下一起打包
前端·spring boot·后端
恋猫de小郭1 小时前
2026,Android Compose 终于支持 Hot Reload 了,但是收费
android·前端·flutter
hpoenixf7 小时前
2026 年前端面试问什么
前端·面试
还是大剑师兰特7 小时前
Vue3 中的 defineExpose 完全指南
前端·javascript·vue.js
泯泷7 小时前
阶段一:从 0 看懂 JSVMP 架构,先在脑子里搭出一台最小 JSVM
前端·javascript·架构
mengchanmian8 小时前
前端node常用配置
前端
monsion8 小时前
OpenCode 学习指南
人工智能·vscode·架构
华洛8 小时前
利好打工人,openclaw不是企业提效工具,而是个人助理
前端·javascript·产品经理
无羡仙8 小时前
实测 Claude 多 Agent 开发:项目经理开局摸鱼,我成了救火队员
架构
xkxnq9 小时前
第六阶段:Vue生态高级整合与优化(第93天)Element Plus进阶:自定义主题(变量覆盖)+ 全局配置与组件按需加载优化
前端·javascript·vue.js