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

讨论题 :

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

相关推荐
深蓝AI21 小时前
Meta 开源用了八年的设计系统 astryx:连坐两天 GitHub 总榜第一,组件库开始写给 agent 的说明书
前端·人工智能
IT_陈寒1 天前
Vue的watch把我坑惨了,原来要这样用才靠谱
前端·人工智能·后端
1-1=01 天前
Vue 3 模板语
前端·javascript·vue.js
用户7610304473711 天前
【转战智能眼镜开发】ROKID AIUI 小游戏 亚马逊标枪项目实战讲解
前端
风卿1 天前
看着AI写代码太孤独了,来欣赏星云吧:一个DSH 装饰插件
前端·javascript
牧艺1 天前
弹层不用再手算坐标了:CSS Anchor Positioning 从原理到可上线
前端·javascript·css
用户老李1 天前
YOLO 目标检测自学教程 07:YOLOv3——多尺度预测
前端
moshuying1 天前
ESP32 墨水屏金价牌(上):从点亮到把图片推过去
前端
李剑一1 天前
别再抬杠,前端岗已经没了!无论前后端,我们最终都要成“全干”工程师
前端·面试
Highcharts.js1 天前
Highcharts 仪表盘与速度仪图表库介绍
前端·信息可视化·仪表板·highcharts·图表开发·图表库·仪表可视化