前端常用设计模式完整梳理
前端常用设计模式围绕组件化开发、状态管理、DOM操作、异步流程、兼容性处理等核心场景,核心目标是解耦、提升代码复用性、可维护性与扩展性。经典GoF 23种设计模式中,前端高频使用的集中在创建型、结构型、行为型三大类,以下按使用频率排序,包含核心定义、前端落地场景、可直接复用的代码示例,以及主流框架的实际应用。
一、创建型设计模式
核心解决对象创建的逻辑封装问题,降低创建复杂度,避免重复代码,统一实例创建规范。
1. 单例模式(Singleton)【最高频】
核心定义
保证一个类/对象在全局只有唯一实例,并提供一个全局访问点,避免重复创建带来的内存浪费和状态不一致。
前端高频场景
- 全局状态管理(Pinia/Vuex/Redux的Store)
- 全局弹窗/Toast(避免多个弹窗叠加)
- 全局Axios实例、WebSocket长连接
- 全局配置对象、日志工具
代码示例(业务场景:全局登录弹窗)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| class LoginModal { static #instance = null;
constructor() { if (LoginModal.#instance) { return LoginModal.#instance; } this.isShow = false; LoginModal.#instance = this; }
show() { if (this.isShow) return; this.isShow = true; console.log("登录弹窗已显示"); }
hide() { this.isShow = false; console.log("登录弹窗已隐藏"); } }
const modal1 = new LoginModal(); const modal2 = new LoginModal(); console.log(modal1 === modal2); modal1.show(); modal2.show();
|
2. 原型模式(Prototype)【JS原生核心】
核心定义
以现有对象为原型,通过克隆拷贝创建新对象,无需关心对象的创建细节。JavaScript是基于原型的语言,原生支持该模式,是JS继承的核心基础。
前端高频场景
- JS原型链与继承实现
Object.create() 创建新对象
- 组件原型方法复用
- 对象深拷贝/浅拷贝实现
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const userPrototype = { init(name, age) { this.name = name; this.age = age; }, sayHello() { console.log(`我是${this.name},今年${this.age}岁`); } };
const user1 = Object.create(userPrototype); user1.init("张三", 25); user1.sayHello();
console.log(user1.__proto__ === userPrototype);
|
3. 工厂模式(Factory)【高频】
核心定义
封装对象的创建逻辑,对外提供统一的创建接口,根据传入参数动态生成不同实例,使用者无需关心内部创建细节。前端最常用的是简单工厂模式。
前端高频场景
- 动态组件渲染(根据type渲染不同业务组件)
- 表单控件生成(根据配置生成输入框、下拉框、单选框)
- 接口请求适配器(适配不同环境的请求方式)
- 多场景日志工具
代码示例(表单控件生成)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| class FormControlFactory { static createControl(type, config) { switch (type) { case "input": return new InputControl(config); case "select": return new SelectControl(config); case "radio": return new RadioControl(config); default: throw new Error("不支持的控件类型"); } } }
class InputControl { constructor(config) { this.type = "input"; this.label = config.label; this.placeholder = config.placeholder; } render() { console.log(`渲染输入框:${this.label}`); } } class SelectControl { constructor(config) { this.type = "select"; this.label = config.label; this.options = config.options; } render() { console.log(`渲染下拉框:${this.label}`); } } class RadioControl { constructor(config) { this.type = "radio"; this.label = config.label; this.options = config.options; } render() { console.log(`渲染单选框:${this.label}`); } }
const nameInput = FormControlFactory.createControl("input", { label: "姓名", placeholder: "请输入姓名" }); const genderRadio = FormControlFactory.createControl("radio", { label: "性别", options: ["男", "女"] }); nameInput.render(); genderRadio.render();
|
4. 建造者模式(Builder)【常用】
核心定义
分步构建一个复杂对象,将构建过程和最终表示分离,相同的构建过程可以创建不同的表示,适合创建属性多、配置复杂的对象。
前端高频场景
- 链式调用API(jQuery、Lodash链式调用)
- 复杂表单/页面的配置构建
- Webpack/Vite构建配置
- 富文本编辑器内容构建
代码示例(链式表单构建器)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| class FormBuilder { constructor() { this.formConfig = { title: "", items: [], submitText: "提交", rules: {} }; }
setTitle(title) { this.formConfig.title = title; return this; }
addInputItem(label, field, placeholder = "") { this.formConfig.items.push({ type: "input", label, field, placeholder }); return this; }
addSelectItem(label, field, options = []) { this.formConfig.items.push({ type: "select", label, field, options }); return this; }
setSubmitText(text) { this.formConfig.submitText = text; return this; }
addRule(field, rule) { this.formConfig.rules[field] = rule; return this; }
build() { return this.formConfig; } }
const userForm = new FormBuilder() .setTitle("用户信息表单") .addInputItem("姓名", "name", "请输入姓名") .addInputItem("手机号", "phone", "请输入手机号") .addSelectItem("城市", "city", ["北京", "上海", "广州", "深圳"]) .setSubmitText("保存信息") .addRule("phone", { required: true, pattern: /^1[3-9]\d{9}$/ }) .build();
console.log(userForm);
|
二、结构型设计模式
核心解决类/对象的组合与结构优化问题,处理对象间的耦合、功能扩展、兼容性适配等场景。
1. 代理模式(Proxy)【最高频】
核心定义
为目标对象提供一个代理对象,通过代理控制对目标对象的访问,可在访问前后添加额外逻辑,不修改原对象代码,符合开闭原则。
前端高频场景
- Vue3响应式原理(Proxy实现数据劫持)
- 事件代理(事件委托,前端DOM操作基础)
- 虚拟代理(图片懒加载、虚拟滚动、预加载)
- 缓存代理(接口请求结果缓存)
- 保护代理(路由守卫、接口权限控制)
代码示例
示例1:前端最基础的事件代理
1 2 3 4 5 6 7 8 9 10 11 12 13
| <ul id="list"> <li>item 1</li> <li>item 2</li> <li>item 3</li> </ul> <script>
document.getElementById('list').addEventListener('click', (e) => { if (e.target.tagName === 'LI') { console.log('点击了', e.target.textContent); } }); </script>
|
示例2:Vue3响应式极简实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| function reactive(target) { return new Proxy(target, { get(target, key) { console.log(`读取属性:${key}`); const value = target[key]; return typeof value === 'object' && value !== null ? reactive(value) : value; }, set(target, key, value) { console.log(`修改属性:${key},新值:${value}`); target[key] = value; return true; }, deleteProperty(target, key) { console.log(`删除属性:${key}`); delete target[key]; return true; } }); }
const user = reactive({ name: "张三", age: 25 }); user.name; user.age = 26;
|
2. 装饰器模式(Decorator)【高频】
核心定义
动态给对象/类添加额外功能,不改变其原有的结构和核心逻辑,比继承更灵活,是对扩展开放、对修改关闭原则的经典实现。
前端高频场景
- ES7/TS装饰器语法(React的
@connect、Vue的@Prop、NestJS路由装饰器)
- React高阶组件(HOC)
- 节流/防抖函数
- 日志埋点、权限校验装饰器
代码示例
示例1:防抖装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function debounceDecorator(fn, delay = 500) { let timer = null; return function (...args) { clearTimeout(timer); timer = setTimeout(() => { fn.apply(this, args); }, delay); }; }
function handleInput(value) { console.log('输入内容:', value); }
const debouncedHandleInput = debounceDecorator(handleInput, 300);
debouncedHandleInput("1"); debouncedHandleInput("12"); debouncedHandleInput("123");
|
示例2:React高阶组件(HOC)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| function withLog(Component) { return class extends React.Component { componentDidMount() { console.log(`组件${Component.name}已挂载`); } componentWillUnmount() { console.log(`组件${Component.name}已卸载`); } render() { return <Component {...this.props} />; } }; }
class UserList extends React.Component { render() { return <div>用户列表</div>; } }
const UserListWithLog = withLog(UserList);
|
3. 适配器模式(Adapter)【高频】
核心定义
将一个类/接口的不兼容格式,转换成使用者需要的兼容格式,让原本无法协同工作的两个对象可以正常配合,核心解决兼容性问题。
前端高频场景
- 后端接口数据格式适配(不同接口的返回格式统一转换成前端需要的结构)
- Axios适配浏览器XMLHttpRequest和Node.js的http模块
- 浏览器API兼容(如
addEventListener和IE的attachEvent)
- 第三方UI组件库的二次封装,适配业务需求
代码示例(接口数据适配)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
function userDataAdapter(originData) { return { userId: originData.user_id || originData.userId, userName: originData.user_name || originData.userName, phoneNumber: originData.phone_number || originData.phoneNumber, avatarUrl: originData.avatar_url || originData.avatarUrl }; }
const oldApiData = { user_id: 1, user_name: "张三", phone_number: "13800138000", avatar_url: "https://xxx.com/avatar.jpg" };
const newApiData = { userId: 2, userName: "李四", phoneNumber: "13900139000", avatarUrl: "https://xxx.com/avatar2.jpg" };
const user1 = userDataAdapter(oldApiData); const user2 = userDataAdapter(newApiData); console.log(user1, user2);
|
4. 外观模式(Facade)【高频】
核心定义
封装内部复杂的子系统逻辑,对外提供一个统一、简单的高层接口,使用者无需关心内部复杂的实现细节,降低使用成本。
前端高频场景
- jQuery(封装DOM操作、兼容性处理,对外提供极简的
$API)
- 统一请求方法封装(处理请求头、错误拦截、序列化、超时,对外仅提供
get/post)
- Storage工具封装(处理序列化、过期时间、加密,对外仅提供
get/set/remove)
- 跨端API兼容封装(适配H5、小程序、APP的不同原生API)
代码示例(Storage工具封装)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| const StorageFacade = { set(key, value, expireHours = null) { try { const data = { value, expire: expireHours ? Date.now() + expireHours * 3600 * 1000 : null }; localStorage.setItem(key, JSON.stringify(data)); } catch (e) { console.error("Storage存储失败", e); } },
get(key) { try { const dataStr = localStorage.getItem(key); if (!dataStr) return null; const data = JSON.parse(dataStr); if (data.expire && Date.now() > data.expire) { localStorage.removeItem(key); return null; } return data.value; } catch (e) { console.error("Storage读取失败", e); return null; } },
remove(key) { try { localStorage.removeItem(key); } catch (e) { console.error("Storage删除失败", e); } } };
StorageFacade.set("token", "xxx-xxx-xxx", 24); const token = StorageFacade.get("token"); StorageFacade.remove("token");
|
5. 组合模式(Composite)【组件化核心】
核心定义
将对象组合成树形结构,以表示「整体-部分」的层次关系,让使用者对单个对象和组合对象的使用具有一致性。
前端高频场景
- React/Vue的虚拟DOM树、组件树
- 树形菜单、组织架构、文件夹结构渲染
- 表单的Form组件与FormItem组件
代码示例(树形菜单渲染)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
|
class MenuItem { constructor(name, path) { this.name = name; this.path = path; }
render(level = 0) { const indent = " ".repeat(level); console.log(`${indent}├─ ${this.name} → ${this.path}`); } }
class MenuFolder { constructor(name) { this.name = name; this.children = []; }
add(child) { this.children.push(child); return this; }
render(level = 0) { const indent = " ".repeat(level); console.log(`${indent}├─ 📁 ${this.name}`); this.children.forEach(child => child.render(level + 1)); } }
const menu = new MenuFolder("系统管理") .add(new MenuItem("用户管理", "/system/user")) .add(new MenuItem("角色管理", "/system/role")) .add( new MenuFolder("权限管理") .add(new MenuItem("菜单权限", "/system/permission/menu")) .add(new MenuItem("按钮权限", "/system/permission/button")) );
menu.render();
|
三、行为型设计模式
核心解决对象间的通信、职责分配、行为控制问题,处理对象间的耦合、流程控制、状态管理等场景。
1. 发布-订阅模式(Publish-Subscribe)【最高频】
核心定义
在发布者和订阅者之间,引入一个事件中心/消息总线,发布者通过事件中心发布事件,订阅者通过事件中心订阅事件,发布者和订阅者完全解耦、互不感知,是观察者模式的进阶变种。
前端高频场景
- 跨组件通信(Vue的EventBus、React的Context、mitt库)
- Node.js的EventEmitter
- 小程序/uni-app的全局事件通信
- 原生DOM自定义事件
代码示例(通用EventBus实现)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| class EventEmitter { constructor() { this.events = Object.create(null); }
on(eventName, callback) { if (!this.events[eventName]) { this.events[eventName] = []; } this.events[eventName].push(callback); }
emit(eventName, ...args) { const callbacks = this.events[eventName]; if (!callbacks) return; callbacks.forEach(cb => cb.apply(this, args)); }
off(eventName, callback) { if (!this.events[eventName]) return; this.events[eventName] = this.events[eventName].filter(cb => cb !== callback); }
once(eventName, callback) { const onceCallback = (...args) => { callback.apply(this, args); this.off(eventName, onceCallback); }; this.on(eventName, onceCallback); } }
const eventBus = new EventEmitter();
const onUserLogin = (userInfo) => { console.log('用户登录成功', userInfo); }; eventBus.on('user-login', onUserLogin);
eventBus.emit('user-login', { userId: 1, userName: "张三" });
eventBus.off('user-login', onUserLogin);
|
2. 观察者模式(Observer)【高频】
核心定义
定义对象间一对多的依赖关系,当一个对象(主题Subject)的状态发生改变时,所有依赖它的观察者(Observer)都会收到通知并自动执行更新。
与发布-订阅模式的核心区别
| 特性 |
观察者模式 |
发布-订阅模式 |
| 耦合度 |
主题和观察者直接耦合,互相感知 |
发布者和订阅者完全解耦,通过事件中心中转 |
| 角色数量 |
2个:主题、观察者 |
3个:发布者、事件中心、订阅者 |
| 执行时机 |
同步执行 |
可同步可异步 |
| 适用场景 |
同应用内的状态监听 |
跨模块、跨应用的消息通信 |
前端高频场景
- 原生DOM的
addEventListener事件监听
- Vue的
watch/computed
- React的
useEffect状态监听
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
|
class Subject { constructor() { this.observers = []; this.state = null; }
addObserver(observer) { this.observers.push(observer); }
removeObserver(observer) { this.observers = this.observers.filter(o => o !== observer); }
setState(newState) { this.state = newState; this.notify(); }
notify() { this.observers.forEach(observer => observer.update(this.state)); } }
class Observer { constructor(name) { this.name = name; }
update(state) { console.log(`${this.name} 收到状态更新:${state}`); } }
const userSubject = new Subject(); const obs1 = new Observer("组件A"); const obs2 = new Observer("组件B");
userSubject.addObserver(obs1); userSubject.addObserver(obs2);
userSubject.setState("用户已登录");
|
3. 职责链模式(Chain of Responsibility)【高频】
核心定义
将请求的发送者和接收者解耦,让多个对象都有机会处理这个请求,将这些对象连成一条链,请求沿着这条链传递,直到有一个对象处理它为止。
前端高频场景
- Axios的请求/响应拦截器
- Koa/Express的洋葱模型中间件
- JS的事件冒泡与捕获
- 前端路由守卫
- 表单多级校验
代码示例(模拟Axios拦截器)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| class Axios { constructor() { this.interceptors = { request: new InterceptorManager(), response: new InterceptorManager() }; }
request(config) { const chain = [this.dispatchRequest.bind(this), undefined]; this.interceptors.request.handlers.forEach(handler => { chain.unshift(handler.fulfilled, handler.rejected); }); this.interceptors.response.handlers.forEach(handler => { chain.push(handler.fulfilled, handler.rejected); });
let promise = Promise.resolve(config); while (chain.length) { promise = promise.then(chain.shift(), chain.shift()); } return promise; }
dispatchRequest(config) { console.log('发起请求', config); return Promise.resolve({ data: '响应数据', status: 200 }); } }
class InterceptorManager { constructor() { this.handlers = []; }
use(fulfilled, rejected) { this.handlers.push({ fulfilled, rejected }); } }
const axios = new Axios();
axios.interceptors.request.use(config => { config.headers = { token: 'xxx' }; console.log('请求拦截器:添加token'); return config; });
axios.interceptors.response.use(res => { console.log('响应拦截器:处理响应数据'); return res.data; });
axios.request({ url: '/api/user', method: 'get' });
|
4. 策略模式(Strategy)【高频】
核心定义
定义一系列独立的算法/策略,将每一个算法封装起来,使它们可以互相替换,算法的变化独立于使用算法的使用者,核心是消除大量的if-else/switch语句。
前端高频场景
- 表单校验(不同校验规则封装成独立策略)
- 支付方式切换(微信/支付宝/银行卡)
- 不同环境的请求地址配置
- 数据格式化(日期、金额、百分比)
代码示例(表单校验)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
|
const validateStrategies = { required(value, errorMsg) { if (!value || value.trim() === '') { return errorMsg; } }, phone(value, errorMsg) { const pattern = /^1[3-9]\d{9}$/; if (!pattern.test(value)) { return errorMsg; } }, minLength(value, length, errorMsg) { if (value.length < length) { return errorMsg; } } };
class FormValidator { constructor() { this.rules = []; }
addRule(field, strategy, ...params) { this.rules.push({ field, strategy, params }); }
validate(formData) { const errors = {}; this.rules.forEach(rule => { const { field, strategy, params } = rule; const value = formData[field]; const errorMsg = validateStrategies[strategy](value, ...params); if (errorMsg) { errors[field] = errorMsg; } }); return { valid: Object.keys(errors).length === 0, errors }; } }
const validator = new FormValidator();
validator.addRule('phone', 'required', '手机号不能为空'); validator.addRule('phone', 'phone', '手机号格式不正确'); validator.addRule('password', 'minLength', 6, '密码长度不能少于6位');
const formData = { phone: '1380013800', password: '123' }; const result = validator.validate(formData); console.log(result);
|
5. 状态模式(State)【常用】
核心定义
一个对象的行为由它的状态决定,当状态发生改变时,行为也随之改变,将不同状态的行为封装成独立的状态类,消除大量的状态判断if-else。
前端高频场景
- 订单状态流转(待付款/待发货/待收货/已完成/已取消)
- 视频播放器状态(播放/暂停/缓冲/结束/报错)
- 表单状态(可编辑/禁用/提交中/校验失败)
- 有限状态机(XState)
代码示例(视频播放器)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
|
class PlayState { constructor(player) { this.player = player; } play() { console.log('暂停播放'); this.player.setState(this.player.pauseState); } }
class PauseState { constructor(player) { this.player = player; } play() { console.log('开始播放'); this.player.setState(this.player.playState); } }
class BufferState { constructor(player) { this.player = player; } play() { console.log('视频缓冲中,无法操作'); } }
class EndState { constructor(player) { this.player = player; } play() { console.log('重新播放'); this.player.setState(this.player.playState); } }
class VideoPlayer { constructor() { this.playState = new PlayState(this); this.pauseState = new PauseState(this); this.bufferState = new BufferState(this); this.endState = new EndState(this); this.currentState = this.pauseState; }
setState(state) { this.currentState = state; }
handlePlayClick() { this.currentState.play(); } }
const player = new VideoPlayer(); player.handlePlayClick(); player.handlePlayClick(); player.setState(player.bufferState); player.handlePlayClick(); player.setState(player.endState); player.handlePlayClick();
|
四、主流前端框架中的设计模式落地
| 设计模式 |
Vue 2/3 落地场景 |
React 落地场景 |
| 代理模式 |
Vue2 Object.defineProperty、Vue3 Proxy 响应式实现 |
useMemo 缓存代理、useCallback 函数代理 |
| 发布-订阅模式 |
EventBus、mitt、Pinia 状态通知 |
Context、Redux、EventEmitter |
| 单例模式 |
Vuex/Pinia Store、全局Router实例 |
Redux Store、全局Context Provider |
| 装饰器模式 |
Vue3 组合式API装饰器、@Prop/@Watch |
高阶组件HOC、@connect、类组件装饰器 |
| 组合模式 |
组件树、虚拟DOM |
组件树、Fiber架构、虚拟DOM |
| 职责链模式 |
路由守卫、Axios拦截器 |
路由拦截器、Redux中间件 |
| 策略模式 |
内置指令、表单校验规则 |
表单校验、条件渲染策略 |
五、选型避坑核心原则
- 场景优先,避免过度设计:简单逻辑不要硬套设计模式,比如单个简单判断无需硬套策略模式,优先保证代码可读性。
- 开闭原则为核心:所有设计模式的最终目标都是「对扩展开放,对修改关闭」,不要为了用模式而破坏这个核心原则。
- 解耦适度:不要为了极致解耦,把代码拆得过于碎片化,反而增加维护成本。
- 优先复用框架原生能力:比如Vue3的响应式已经基于代理模式实现,不要重复造轮子;React的HOC已经是装饰器模式的最佳实践,优先使用。