共计 2727 个字符,预计需要花费 7 分钟才能阅读完成。
背景痛点:表单验证的维护噩梦
在电商订单提交、金融开户等多步骤表单中,我们常遇到这类代码:

function validateForm(data) {if (!data.username) {return '用户名必填';} else if (data.username.length < 6) {return '用户名太短';} else if (!/^[a-z0-9]+$/i.test(data.username)) {return '只能包含字母数字';}
if (data.password.length < 8) {return '密码需 8 位以上';}
// 更多嵌套判断...
}
传统方案存在三大痛点:
- 修改风险高:新增规则可能意外影响其他校验
- 复用困难:相同规则(如手机号校验)需重复编写
- 可读性差:数百行的 if-else 难以维护
技术选型:策略模式为何胜出
1. 与责任链模式对比
责任链模式虽然也能解耦校验逻辑,但存在两个关键差异:
- 责任链强调『传递处理』,适合需要按顺序检查的场景
- 策略模式更注重『算法替换』,适合独立校验规则
2. 与工厂模式对比
工厂模式关注对象创建,而策略模式专注行为封装。表单验证的核心需求是动态切换校验算法,这正是策略模式的强项。
核心实现:TypeScript 策略模式实战
第一步:定义策略接口
interface ValidationStrategy<T = any> {validate(value: T, context?: object): string | null;
}
第二步:实现具体策略
class RequiredValidation implements ValidationStrategy {validate(value: string) {return value ? null : '该字段必填';}
}
class LengthValidation implements ValidationStrategy<string> {constructor(private min: number, private max?: number) {}
validate(value: string) {if (value.length < this.min)
return ` 至少需要 ${this.min}个字符 `;
if (this.max && value.length > this.max)
return ` 不能超过 ${this.max}个字符 `;
return null;
}
}
第三步:构建策略上下文
class Validator {private strategies = new Map<string, ValidationStrategy>();
addStrategy(field: string, strategy: ValidationStrategy) {this.strategies.set(field, strategy);
return this; // 支持链式调用
}
validate(formData: Record<string, any>) {const errors: Record<string, string> = {};
this.strategies.forEach((strategy, field) => {const error = strategy.validate(formData[field], formData);
if (error) errors[field] = error;
});
return Object.keys(errors).length ? errors : null;
}
}
动态切换示例
// 根据不同场景切换验证规则
const loginValidator = new Validator()
.addStrategy('username', new LengthValidation(6))
.addStrategy('password', new LengthValidation(8));
const registerValidator = new Validator()
.addStrategy('email', new EmailValidation());
性能优化
内存优势
- 策略对象可复用:一个
LengthValidation实例能服务所有长度校验字段 - 避免重复创建正则:相比直接写
/^[a-z]+$/i,策略类能缓存正则实例
基准测试对比
| 验证方式 | 10 万次执行耗时 |
|---|---|
| 直接正则校验 | 48ms |
| 策略模式 | 52ms |
| if-else 嵌套 | 65ms |
差异主要来自策略模式的方法调用开销,但在实际业务中可忽略不计。
避坑指南
1. 避免类爆炸
错误做法:
// 为每个字段创建单独策略类
class UsernameRequiredValidation implements ValidationStrategy {}
class PasswordRequiredValidation implements ValidationStrategy {}
正确做法:通过配置化复用策略
// 复用 RequiredValidation
validator.addStrategy('username', new RequiredValidation());
2. 控制上下文边界
策略类不应直接修改表单数据,两种安全通信方式:
- 只读上下文 :将表单数据作为 validate() 参数传入
- 事件机制:通过发布订阅通知错误
单元测试示例
describe('LengthValidation', () => {const strategy = new LengthValidation(5, 10);
test('允许 6 个字符', () => {expect(strategy.validate('123456')).toBeNull();});
test('拒绝 4 个字符', () => {expect(strategy.validate('1234'))
.toMatch('至少需要 5 个字符');
});
});
进阶思考
异步验证策略
如何处理需要调用 API 的用户名校验?尝试改造接口:
interface AsyncValidationStrategy {validate(value: any): Promise<string | null>;
}
复合策略模式
组合多个策略实现复杂校验:
validator.addStrategy('password',
new CompositeStrategy([new RequiredValidation(),
new LengthValidation(8),
new ComplexityValidation()])
);
总结
策略模式在前端表单验证中展现出三大优势:
- 开闭原则:新增校验规则不影响现有代码
- 可测试性:每个策略类可独立单元测试
- 可视化配置:验证规则可 JSON 化存储
下次遇到复杂表单时,不妨试试这种『分而治之』的验证方案。
正文完
发表至: 前端开发
近一天内
