前端常用的设计模式

前端常用设计模式完整梳理

前端常用设计模式围绕组件化开发、状态管理、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("登录弹窗已显示");
// 实际DOM渲染逻辑
}

hide() {
this.isShow = false;
console.log("登录弹窗已隐藏");
}
}

// 验证:无论实例化多少次,永远是同一个对象
const modal1 = new LoginModal();
const modal2 = new LoginModal();
console.log(modal1 === modal2); // true
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(); // 我是张三,今年25岁

// 验证:新对象继承自原型
console.log(user1.__proto__ === userPrototype); // true

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>
// 优势:1. 减少内存占用,无需给每个子元素绑定事件;2. 动态新增的元素自动生效
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
// 代理模式:基于Proxy实现响应式
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; // 读取属性:name
user.age = 26; // 修改属性: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);
// 频繁调用只会在停止输入300ms后执行一次
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
// 装饰器模式:React高阶组件,给组件添加日志能力
function withLog(Component) {
return class extends React.Component {
componentDidMount() {
console.log(`组件${Component.name}已挂载`);
}
componentWillUnmount() {
console.log(`组件${Component.name}已卸载`);
}
render() {
// 透传props,不修改原组件逻辑
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
// 外观模式:封装复杂的Storage操作,对外提供简单统一的接口
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); // 存储token,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
// 组合模式:树形菜单,叶子节点和组合节点使用统一的render接口
// 叶子节点:菜单项
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"))
);

// 统一调用render方法,无需关心是目录还是菜单项
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() {
// 事件中心:key为事件名,value为回调函数数组
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();

// 组件A:订阅用户登录事件
const onUserLogin = (userInfo) => {
console.log('用户登录成功', userInfo);
};
eventBus.on('user-login', onUserLogin);

// 组件B:发布用户登录事件
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("用户已登录");
// 输出:
// 组件A 收到状态更新:用户已登录
// 组件B 收到状态更新:用户已登录

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
// 职责链模式:模拟Axios拦截器实现
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
// 策略模式:表单校验,消除if-else
// 1. 定义策略对象:封装所有校验规则
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;
}
}
};

// 2. 环境类:使用策略的校验器
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);
// 输出:
// {
// valid: false,
// errors: {
// phone: '手机号格式不正确',
// password: '密码长度不能少于6位'
// }
// }

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
// 状态模式:视频播放器,不同状态对应不同行为
// 1. 定义状态类,封装每个状态的行为
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);
}
}

// 2. 播放器上下文
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中间件
策略模式 内置指令、表单校验规则 表单校验、条件渲染策略

五、选型避坑核心原则

  1. 场景优先,避免过度设计:简单逻辑不要硬套设计模式,比如单个简单判断无需硬套策略模式,优先保证代码可读性。
  2. 开闭原则为核心:所有设计模式的最终目标都是「对扩展开放,对修改关闭」,不要为了用模式而破坏这个核心原则。
  3. 解耦适度:不要为了极致解耦,把代码拆得过于碎片化,反而增加维护成本。
  4. 优先复用框架原生能力:比如Vue3的响应式已经基于代理模式实现,不要重复造轮子;React的HOC已经是装饰器模式的最佳实践,优先使用。

前端常用的设计模式
https://cszy.top/20250829-前端常用的设计模式/
作者
csorz
发布于
2025年8月29日
许可协议