跳到主要内容

前端编程模式入门

你在写前端业务时,是否遇到过这些问题:

  • 组件之间通信越来越乱,不知道数据从哪里来、往哪里去。
  • 一段逻辑在多个页面反复出现,但每次都复制粘贴改一点。
  • 需求一变,改动像“牵一发而动全身”,容易引入回归问题。

这时候就需要「编程模式(Programming Patterns)」来帮助我们组织代码。

一、什么是编程模式?

可以把编程模式理解为:在某类问题下,经实践验证过的“高复用解法模板”

它不是死板套路,而是帮助你在复杂业务里做出更稳定、更可维护的设计选择。

二、前端为什么要学编程模式?

场景常见痛点对应收益
组件复用逻辑分散、重复实现提升复用率,减少重复代码
功能扩展每次加需求都改很多旧代码降低改动范围,提升可扩展性
团队协作每个人写法不同,理解成本高形成统一设计语言
故障排查边界不清晰,定位慢结构清楚,问题更易定位

三、前端中高频模式速览

先建立全局地图,再逐个深入。

四、两个最常见模式:先看懂再记名词

4.1 观察者 / 发布订阅模式

适用场景: 组件通信、事件系统、状态变化通知。

// 一个极简事件总线示例
const listeners: Record<string, Array<(payload: unknown) => void>> = {}

export function on(event: string, handler: (payload: unknown) => void) {
listeners[event] ??= []
listeners[event].push(handler)
}

export function emit(event: string, payload: unknown) {
listeners[event]?.forEach((handler) => handler(payload))
}

你可以把它理解成“订阅频道”:订阅者不关心消息来自哪里,只关心自己订阅的话题。

4.2 策略模式

适用场景: 同一目标下有多种算法/规则可替换(如价格计算、表单校验、权限判断)。

type DiscountStrategy = (price: number) => number

const strategies: Record<string, DiscountStrategy> = {
normal: (price) => price,
vip: (price) => price * 0.9,
student: (price) => price * 0.85,
}

export function calcPrice(price: number, level: 'normal' | 'vip' | 'student') {
return strategies[level](price)
}

核心价值:把“会变化的规则”独立出去,避免大量 if/else 混在业务主流程里。

五、学习建议(面向初学者)

  1. 先从问题出发:不要背定义,先问“我现在遇到的痛点是什么”。
  2. 每个模式写一个最小可运行例子:10~30 行即可。
  3. 做反例对比:先写“没有模式”的版本,再重构成“有模式”。
  4. 结合真实业务复盘:记录一次你用模式减少改动范围的经历。

六、实践任务(可直接动手)

你可以做一个「订单价格计算器」练习:

  • 先写一个 if/else 版本(普通/会员/学生价)。
  • 再重构为策略模式。
  • 最后新增“节日折扣”策略,观察改动范围。

通过这个练习,你会直观感受到“可扩展”到底在解决什么问题。

面试高频问答

1) 编程模式和设计模式有什么区别?

日常语境里两者经常混用。更严格地说,设计模式通常指 GoF 那套经典模式;编程模式是更宽泛概念,包含设计模式以及前端工程里常见的组织方式(如状态管理模式、组合模式等)。

2) 什么时候不应该强行上模式?

当业务很小、变化不频繁、模式引入后复杂度明显高于收益时,不建议上。模式是为了解决问题,不是为了“显得高级”。

3) 发布订阅和观察者模式是同一个东西吗?

思想接近,都是“变化通知”。但发布订阅通常通过事件中心解耦发布者和订阅者;经典观察者里,被观察对象直接维护观察者列表,耦合相对更直接。

4) 策略模式相比 if/else 最大优势是什么?

把可变规则独立封装,新增策略时通常只需扩展,不需要修改主流程逻辑,符合开闭原则,测试也更容易拆分。

5) 前端最值得优先掌握哪些模式?

建议优先:观察者/发布订阅、策略、组合、工厂。它们在组件通信、业务规则扩展、UI 结构组织、对象创建中都非常高频。