

在HarmonyOS应用开发中,表单验证是保障数据质量和用户体验的关键环节。一个健壮的表单验证系统不仅能够有效防止无效数据进入后端系统,还能为用户提供即时、友好的反馈,提升整体交互体验。本文将深入探讨表单验证的核心原理、实现策略和最佳实践。
一、表单验证的重要性与挑战
1.1 表单验证的意义
表单验证在应用开发中具有多重意义:
- 数据完整性:确保必填字段不缺失
- 数据正确性:验证数据格式符合预期
- 安全性:防止恶意输入和注入攻击
- 用户体验:提供即时反馈,减少错误提交
- 后端保护:减轻服务器端验证压力
1.2 表单验证的挑战
在实际开发中,表单验证面临诸多挑战:
- 多平台适配:不同设备和屏幕尺寸的适配
- 实时验证性能:高频输入场景下的性能优化
- 复杂业务规则:多字段联动验证逻辑
- 国际化支持:不同地区的格式差异(如手机号、身份证等)
- 可维护性:验证规则的灵活扩展和管理
二、验证规则体系设计
2.1 验证规则分类
根据验证的复杂度和用途,可将验证规则分为以下几类:
| 类别 | 说明 | 示例 |
|---|---|---|
| 格式验证 | 验证数据格式是否符合规范 | 邮箱、手机号、URL |
| 长度验证 | 验证字符串长度范围 | 密码长度、用户名长度 |
| 数值验证 | 验证数值范围 | 年龄、金额、数量 |
| 逻辑验证 | 验证业务逻辑规则 | 密码确认、日期范围 |
| 自定义验证 | 特定业务场景的验证 | 身份证校验、企业税号 |
2.2 验证结果接口设计
typescript
export interface ValidationResult {
valid: boolean;
message: string;
}
该接口定义了验证结果的基本结构:
valid:布尔值,表示验证是否通过message:字符串,包含验证失败时的错误提示信息
三、核心验证规则实现
3.1 邮箱验证
3.1.1 正则表达式设计
typescript
export function isEmail(value: string): boolean {
const regex: RegExp = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(value);
}
正则表达式解析:
^[^\s@]+:匹配邮箱用户名部分,不包含空格和@符号@:匹配@符号[^\s@]+:匹配域名部分\.[^\s@]+$:匹配顶级域名
3.1.2 完整验证函数
typescript
export function validateEmail(value: string): ValidationResult {
if (!value) {
return { valid: false, message: '请输入邮箱地址' };
}
if (!isEmail(value)) {
return { valid: false, message: '请输入有效的邮箱地址,如: example@email.com' };
}
return { valid: true, message: '✓ 邮箱格式正确' };
}
验证逻辑:
- 检查是否为空
- 检查格式是否符合邮箱规范
- 返回相应的验证结果
3.2 手机号验证
3.2.1 正则表达式设计
typescript
export function isPhone(value: string): boolean {
const regex: RegExp = /^1[3-9]\d{9}$/;
return regex.test(value);
}
正则表达式解析:
^1:以1开头(中国大陆手机号特征)[3-9]:第二位为3-9(排除1和2)\d{9}$:后面跟着9位数字
3.2.2 完整验证函数
typescript
export function validatePhone(value: string): ValidationResult {
if (!value) {
return { valid: false, message: '请输入手机号码' };
}
if (!isPhone(value)) {
return { valid: false, message: '请输入有效的11位手机号码' };
}
return { valid: true, message: '✓ 手机号码格式正确' };
}
3.3 身份证验证
3.3.1 正则表达式设计
typescript
export function isIdCard(value: string): boolean {
const regex: RegExp = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/;
return regex.test(value);
}
正则表达式解析:
^[1-9]\d{5}:6位行政区划代码(第一位不为0)(18|19|20)\d{2}:年份(1800-2099)(0[1-9]|1[0-2]):月份(01-12)(0[1-9]|[12]\d|3[01]):日期(01-31)\d{3}:顺序码[\dXx]$:校验码(数字或X)
3.3.2 完整验证函数
typescript
export function validateIdCard(value: string): ValidationResult {
if (!value) {
return { valid: false, message: '请输入身份证号码' };
}
if (!isIdCard(value)) {
return { valid: false, message: '请输入有效的18位身份证号码' };
}
return { valid: true, message: '✓ 身份证号码格式正确' };
}
3.4 密码强度验证
3.4.1 密码强度检测
typescript
export function isPasswordStrong(value: string): boolean {
if (value.length < 8) {
return false;
}
const hasUpper = /[A-Z]/.test(value);
const hasLower = /[a-z]/.test(value);
const hasNumber = /\d/.test(value);
const hasSpecial = /[!@#$%^&*()_+\-=\[\]{}|;:,.<>?]/.test(value);
return hasUpper && hasLower && (hasNumber || hasSpecial);
}
密码强度规则:
- 长度至少8位
- 包含大写字母
- 包含小写字母
- 包含数字或特殊字符
3.4.2 密码强度等级评估
typescript
export interface PasswordStrength {
level: number;
label: string;
color: string;
}
export function getPasswordStrength(value: string): PasswordStrength {
let level = 0;
if (value.length >= 6) level++;
if (value.length >= 10) level++;
if (/[a-z]/.test(value)) level++;
if (/[A-Z]/.test(value)) level++;
if (/\d/.test(value)) level++;
if (/[!@#$%^&*()_+\-=\[\]{}|;:,.<>?]/.test(value)) level++;
const result: PasswordStrength = { level: level, label: '', color: '' };
if (level <= 2) {
result.label = '弱';
result.color = '#FF4757';
} else if (level <= 4) {
result.label = '中';
result.color = '#FFA502';
} else {
result.label = '强';
result.color = '#2ED573';
}
return result;
}
强度评估标准:
| 等级 | 分数范围 | 标签 | 颜色 |
|---|---|---|---|
| 弱 | 0-2 | 弱 | 红色 |
| 中 | 3-4 | 中 | 橙色 |
| 强 | 5-6 | 强 | 绿色 |
3.5 长度范围验证
typescript
export function validateLengthRange(value: string, min: number, max: number): ValidationResult {
if (!value) {
return { valid: false, message: '请输入内容' };
}
if (value.length < min) {
return { valid: false, message: `内容长度不能少于${min}个字符` };
}
if (value.length > max) {
return { valid: false, message: `内容长度不能超过${max}个字符` };
}
return { valid: true, message: '✓ 长度符合要求' };
}
3.6 数字验证
typescript
export function isNumber(value: string): boolean {
const regex: RegExp = /^-?\d+(\.\d+)?$/;
return regex.test(value);
}
export function validateNumber(value: string): ValidationResult {
if (!value) {
return { valid: false, message: '请输入数字' };
}
if (!isNumber(value)) {
return { valid: false, message: '请输入有效的数字' };
}
return { valid: true, message: '✓ 数字格式正确' };
}
四、验证策略模式
4.1 策略模式概述
策略模式是一种行为设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以互相替换。在表单验证中,策略模式可以帮助我们:
- 将验证逻辑与UI组件解耦
- 实现验证规则的动态切换
- 提高代码的可维护性和扩展性
4.2 验证策略接口设计
typescript
export interface ValidatorStrategy {
validate(value: string): ValidationResult;
}
4.3 具体策略实现
typescript
export class EmailValidator implements ValidatorStrategy {
validate(value: string): ValidationResult {
if (!value) {
return { valid: false, message: '请输入邮箱地址' };
}
const regex: RegExp = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!regex.test(value)) {
return { valid: false, message: '请输入有效的邮箱地址' };
}
return { valid: true, message: '✓ 邮箱格式正确' };
}
}
export class PhoneValidator implements ValidatorStrategy {
validate(value: string): ValidationResult {
if (!value) {
return { valid: false, message: '请输入手机号码' };
}
const regex: RegExp = /^1[3-9]\d{9}$/;
if (!regex.test(value)) {
return { valid: false, message: '请输入有效的11位手机号码' };
}
return { valid: true, message: '✓ 手机号码格式正确' };
}
}
export class RequiredValidator implements ValidatorStrategy {
validate(value: string): ValidationResult {
if (!value || value.trim().length === 0) {
return { valid: false, message: '此字段为必填项' };
}
return { valid: true, message: '' };
}
}
export class LengthValidator implements ValidatorStrategy {
private min: number;
private max: number;
constructor(min: number, max: number) {
this.min = min;
this.max = max;
}
validate(value: string): ValidationResult {
if (!value) {
return { valid: false, message: '请输入内容' };
}
if (value.length < this.min) {
return { valid: false, message: `内容长度不能少于${this.min}个字符` };
}
if (value.length > this.max) {
return { valid: false, message: `内容长度不能超过${this.max}个字符` };
}
return { valid: true, message: '✓ 长度符合要求' };
}
}
4.4 策略上下文
typescript
export class ValidationContext {
private strategies: ValidatorStrategy[] = [];
addStrategy(strategy: ValidatorStrategy): void {
this.strategies.push(strategy);
}
validate(value: string): ValidationResult {
for (let i = 0; i < this.strategies.length; i++) {
const result = this.strategies[i].validate(value);
if (!result.valid) {
return result;
}
}
return { valid: true, message: '✓ 验证通过' };
}
}
4.5 策略模式使用示例
typescript
// 创建验证上下文
const emailContext = new ValidationContext();
emailContext.addStrategy(new RequiredValidator());
emailContext.addStrategy(new EmailValidator());
// 执行验证
const result = emailContext.validate('test@example.com');
console.log(result); // { valid: true, message: '✓ 验证通过' }
五、表单验证组件设计
5.1 验证输入组件
typescript
@ComponentV2
struct ValidatedInput {
@Param label: string = '';
@Param placeholder: string = '';
@Param strategies: ValidatorStrategy[] = [];
@Event onChange: (value: string, result: ValidationResult) => void = () => {};
@Local value: string = '';
@Local result: ValidationResult = { valid: false, message: '' };
build() {
Column({ space: 8 }) {
Text(this.label)
.fontSize(14)
.fontColor('#1C1C1E')
TextInput({ placeholder: this.placeholder, text: this.value })
.width('100%')
.height(44)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.padding({ left: 12, right: 12 })
.onChange((value: string) => {
this.value = value;
this.validate(value);
})
if (this.value) {
Text(this.result.message)
.fontSize(12)
.fontColor(this.result.valid ? '#2ED573' : '#FF4757')
}
}
}
private validate(value: string): void {
for (let i = 0; i < this.strategies.length; i++) {
const strategyResult = this.strategies[i].validate(value);
if (!strategyResult.valid) {
this.result = strategyResult;
this.onChange(value, strategyResult);
return;
}
}
this.result = { valid: true, message: '✓ 验证通过' };
this.onChange(value, this.result);
}
}
5.2 表单验证管理器
typescript
export class FormValidator {
private fields: Map<string, ValidationResult> = new Map();
setFieldResult(fieldName: string, result: ValidationResult): void {
this.fields.set(fieldName, result);
}
getFieldResult(fieldName: string): ValidationResult | undefined {
return this.fields.get(fieldName);
}
isFormValid(): boolean {
for (const [, result] of this.fields) {
if (!result.valid) {
return false;
}
}
return this.fields.size > 0;
}
reset(): void {
this.fields.clear();
}
}
六、实战案例:用户注册表单
6.1 表单数据模型
typescript
interface UserRegistration {
email: string;
phone: string;
password: string;
nickname: string;
age?: string;
}
6.2 表单验证实现
typescript
@Entry
@ComponentV2
struct RegistrationForm {
@Local formData: UserRegistration = {
email: '',
phone: '',
password: '',
nickname: '',
age: ''
};
@Local emailResult: ValidationResult = { valid: false, message: '' };
@Local phoneResult: ValidationResult = { valid: false, message: '' };
@Local passwordResult: ValidationResult = { valid: false, message: '' };
@Local nicknameResult: ValidationResult = { valid: false, message: '' };
@Local ageResult: ValidationResult = { valid: true, message: '' };
@Local passwordStrength: PasswordStrength = { level: 0, label: '', color: '' };
@Local allValid: boolean = false;
build() {
Column({ space: 0 }) {
this.buildHeader();
Scroll() {
Column({ space: 20 }) {
this.buildEmailField();
this.buildPhoneField();
this.buildPasswordField();
this.buildNicknameField();
this.buildAgeField();
this.buildSubmitButton();
}
.width('100%')
.padding(20)
}
.width('100%')
.height('100%')
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F7')
}
@Builder
buildHeader() {
Row() {
Text('用户注册')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#1C1C1E')
}
.width('100%')
.height(56)
.padding({ left: 20 })
.alignItems(VerticalAlign.Center)
.backgroundColor('#FFFFFF')
}
@Builder
buildEmailField() {
Column({ space: 8 }) {
Row({ space: 4 }) {
Text('邮箱')
.fontSize(14)
.fontColor('#1C1C1E')
Text('*')
.fontSize(14)
.fontColor('#FF4757')
}
TextInput({ placeholder: '请输入邮箱地址', text: this.formData.email })
.width('100%')
.height(44)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.padding({ left: 12, right: 12 })
.onChange((value: string) => {
this.formData.email = value;
this.emailResult = validateEmail(value);
this.checkAllValid();
})
if (this.formData.email) {
Text(this.emailResult.message)
.fontSize(12)
.fontColor(this.emailResult.valid ? '#2ED573' : '#FF4757')
}
}
}
@Builder
buildPhoneField() {
Column({ space: 8 }) {
Row({ space: 4 }) {
Text('手机号')
.fontSize(14)
.fontColor('#1C1C1E')
Text('*')
.fontSize(14)
.fontColor('#FF4757')
}
TextInput({ placeholder: '请输入手机号码', text: this.formData.phone })
.width('100%')
.height(44)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.padding({ left: 12, right: 12 })
.type(InputType.Number)
.onChange((value: string) => {
this.formData.phone = value;
this.phoneResult = validatePhone(value);
this.checkAllValid();
})
if (this.formData.phone) {
Text(this.phoneResult.message)
.fontSize(12)
.fontColor(this.phoneResult.valid ? '#2ED573' : '#FF4757')
}
}
}
@Builder
buildPasswordField() {
Column({ space: 8 }) {
Row({ space: 4 }) {
Text('密码')
.fontSize(14)
.fontColor('#1C1C1E')
Text('*')
.fontSize(14)
.fontColor('#FF4757')
}
TextInput({ placeholder: '请输入密码(至少8位)', text: this.formData.password })
.width('100%')
.height(44)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.padding({ left: 12, right: 12 })
.type(InputType.Password)
.onChange((value: string) => {
this.formData.password = value;
this.passwordResult = validatePassword(value);
this.passwordStrength = getPasswordStrength(value);
this.checkAllValid();
})
Row({ space: 8 }) {
if (this.formData.password) {
Text(this.passwordResult.message)
.fontSize(12)
.fontColor(this.passwordResult.valid ? '#2ED573' : '#FF4757')
if (this.passwordStrength.label) {
Text(`强度: ${this.passwordStrength.label}`)
.fontSize(12)
.fontColor(this.passwordStrength.color)
}
}
}
if (this.formData.password) {
Row({ space: 2 }) {
ForEach([1, 2, 3, 4, 5, 6], (index: number) => {
Row()
.width(24)
.height(6)
.borderRadius(3)
.backgroundColor(index <= this.passwordStrength.level ? this.passwordStrength.color : '#E0E0E0')
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
}
}
@Builder
buildNicknameField() {
Column({ space: 8 }) {
Row({ space: 4 }) {
Text('昵称')
.fontSize(14)
.fontColor('#1C1C1E')
Text('*')
.fontSize(14)
.fontColor('#FF4757')
}
TextInput({ placeholder: '请输入昵称(2-10个字符)', text: this.formData.nickname })
.width('100%')
.height(44)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.padding({ left: 12, right: 12 })
.onChange((value: string) => {
this.formData.nickname = value;
this.nicknameResult = validateLengthRange(value, 2, 10);
this.checkAllValid();
})
if (this.formData.nickname) {
Text(this.nicknameResult.message)
.fontSize(12)
.fontColor(this.nicknameResult.valid ? '#2ED573' : '#FF4757')
}
}
}
@Builder
buildAgeField() {
Column({ space: 8 }) {
Text('年龄')
.fontSize(14)
.fontColor('#1C1C1E')
TextInput({ placeholder: '请输入年龄(选填)', text: this.formData.age || '' })
.width('100%')
.height(44)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.padding({ left: 12, right: 12 })
.type(InputType.Number)
.onChange((value: string) => {
this.formData.age = value;
if (value) {
this.ageResult = validateNumber(value);
} else {
this.ageResult = { valid: true, message: '' };
}
this.checkAllValid();
})
if (this.formData.age) {
Text(this.ageResult.message)
.fontSize(12)
.fontColor(this.ageResult.valid ? '#2ED573' : '#FF4757')
}
}
}
@Builder
buildSubmitButton() {
Button('注册')
.width('100%')
.height(48)
.backgroundColor(this.allValid ? '#007DFF' : '#CCCCCC')
.fontColor('#FFFFFF')
.borderRadius(8)
.enabled(this.allValid)
.onClick(() => {
if (this.allValid) {
this.submitForm();
}
})
Text(this.allValid ? '✓ 所有字段验证通过' : '请完善表单信息')
.fontSize(12)
.fontColor(this.allValid ? '#2ED573' : '#98989A')
.width('100%')
.textAlign(TextAlign.Center)
}
private checkAllValid(): void {
this.allValid =
this.emailResult.valid &&
this.phoneResult.valid &&
this.passwordResult.valid &&
this.nicknameResult.valid &&
(this.formData.age ? this.ageResult.valid : true);
}
private submitForm(): void {
console.log('表单提交:', this.formData);
// 调用API提交表单
}
}
七、高级验证技术
7.1 异步验证
在某些场景下,验证需要调用后端API(如检查用户名是否已存在):
typescript
export interface AsyncValidationResult {
valid: boolean;
message: string;
loading: boolean;
}
export async function validateUsername(username: string): Promise<ValidationResult> {
if (!username) {
return { valid: false, message: '请输入用户名' };
}
// 模拟API调用
await new Promise(resolve => setTimeout(resolve, 500));
// 模拟检查结果
const exists = Math.random() > 0.7;
if (exists) {
return { valid: false, message: '该用户名已被注册' };
}
return { valid: true, message: '✓ 用户名可用' };
}
7.2 多字段联动验证
某些验证规则需要多个字段配合:
typescript
export function validatePasswordMatch(password: string, confirmPassword: string): ValidationResult {
if (!confirmPassword) {
return { valid: false, message: '请再次输入密码' };
}
if (password !== confirmPassword) {
return { valid: false, message: '两次输入的密码不一致' };
}
return { valid: true, message: '✓ 密码一致' };
}
export function validateDateRange(startDate: string, endDate: string): ValidationResult {
if (!startDate) {
return { valid: false, message: '请选择开始日期' };
}
if (!endDate) {
return { valid: false, message: '请选择结束日期' };
}
const start = new Date(startDate);
const end = new Date(endDate);
if (end < start) {
return { valid: false, message: '结束日期不能早于开始日期' };
}
return { valid: true, message: '✓ 日期范围有效' };
}
7.3 自定义验证规则
允许用户扩展验证规则:
typescript
export interface CustomRule {
name: string;
regex: RegExp;
errorMessage: string;
}
export function validateWithCustomRule(value: string, rule: CustomRule): ValidationResult {
if (!value) {
return { valid: false, message: '请输入内容' };
}
if (!rule.regex.test(value)) {
return { valid: false, message: rule.errorMessage };
}
return { valid: true, message: '✓ 验证通过' };
}
八、性能优化策略
8.1 防抖优化
对于实时验证场景,使用防抖减少验证次数:
typescript
type SimpleFunction = () => void;
export function debounce(fn: SimpleFunction, delay: number): SimpleFunction {
let timer: number | null = null;
return (): void => {
if (timer !== null) {
clearTimeout(timer);
}
const timerValue = setTimeout(() => {
fn();
}, delay);
timer = timerValue as number;
};
}
使用示例:
typescript
@Local debouncedValidate: () => void = debounce(() => {
this.validateField();
}, 300);
onInputChange(value: string) {
this.value = value;
this.debouncedValidate();
}
8.2 验证缓存
对于复杂验证规则,缓存验证结果:
typescript
export class ValidationCache {
private cache: Map<string, ValidationResult> = new Map();
get(key: string): ValidationResult | undefined {
return this.cache.get(key);
}
set(key: string, result: ValidationResult): void {
this.cache.set(key, result);
}
clear(): void {
this.cache.clear();
}
validate(key: string, value: string, validator: (value: string) => ValidationResult): ValidationResult {
const cached = this.cache.get(key);
if (cached) {
return cached;
}
const result = validator(value);
this.cache.set(key, result);
return result;
}
}
8.3 批量验证
对于包含大量字段的表单,支持批量验证:
typescript
export function validateForm(fields: Record<string, string>, validators: Record<string, (value: string) => ValidationResult>): Record<string, ValidationResult> {
const results: Record<string, ValidationResult> = {};
for (const [fieldName, value] of Object.entries(fields)) {
const validator = validators[fieldName];
if (validator) {
results[fieldName] = validator(value);
}
}
return results;
}
九、验证错误处理与用户反馈
9.1 错误提示样式
typescript
@Builder
buildValidationMessage(result: ValidationResult) {
Row() {
if (result.valid) {
Image($r('app.media.check_icon'))
.width(16)
.height(16)
Text(result.message)
.fontSize(12)
.fontColor('#2ED573')
} else {
Image($r('app.media.error_icon'))
.width(16)
.height(16)
Text(result.message)
.fontSize(12)
.fontColor('#FF4757')
}
}
.alignItems(VerticalAlign.Center)
.space(4)
}
9.2 表单错误汇总
typescript
@Builder
buildErrorSummary(errors: string[]) {
if (errors.length > 0) {
Column({ space: 4 }) {
Text('请修正以下错误:')
.fontSize(14)
.fontColor('#FF4757')
.fontWeight(FontWeight.Bold)
ForEach(errors, (error: string) => {
Row({ space: 4 }) {
Text('•')
.fontSize(12)
.fontColor('#FF4757')
Text(error)
.fontSize(12)
.fontColor('#FF4757')
}
})
}
.padding(12)
.backgroundColor('#FFF0F0')
.borderRadius(8)
}
}
十、测试与质量保障
10.1 单元测试
typescript
import { describe, it, expect } from '@ohos/hypium';
describe('Validator Tests', () => {
it('Email validation', () => {
expect(isEmail('test@example.com')).toBe(true);
expect(isEmail('invalid-email')).toBe(false);
expect(isEmail('')).toBe(false);
});
it('Phone validation', () => {
expect(isPhone('13812345678')).toBe(true);
expect(isPhone('12345678901')).toBe(false);
expect(isPhone('1381234567')).toBe(false);
});
it('Password strength', () => {
expect(isPasswordStrong('Weak123')).toBe(false);
expect(isPasswordStrong('Strong@123')).toBe(true);
expect(isPasswordStrong('Short')).toBe(false);
});
it('ID card validation', () => {
expect(isIdCard('110101199003074512')).toBe(true);
expect(isIdCard('11010119900307451X')).toBe(true);
expect(isIdCard('123456789012345678')).toBe(false);
});
});
10.2 边界条件测试
| 测试场景 | 输入 | 预期结果 |
|---|---|---|
| 空字符串 | '' |
验证失败 |
| 空格字符串 | ' ' |
验证失败 |
| 最小长度 | 'ab' (最小2位) |
验证通过 |
| 最大长度 | 'abcdefghij' (最大10位) |
验证通过 |
| 超出范围 | 'abcdefghijk' (最大10位) |
验证失败 |
| 特殊字符 | 'test@#$%' |
根据规则判断 |
十一、总结
本文深入探讨了HarmonyOS应用中的表单验证机制,涵盖以下核心内容:
- 验证规则设计:详细介绍了邮箱、手机号、身份证、密码等常用验证规则的正则表达式设计
- 策略模式应用:通过策略模式实现验证规则的灵活组合和扩展
- 组件化设计:构建可复用的验证输入组件和表单验证管理器
- 实战案例:完整的用户注册表单实现,展示验证机制的实际应用
- 高级技术:异步验证、多字段联动、自定义规则等高级验证技术
- 性能优化:防抖、缓存、批量验证等性能优化策略
通过本文的学习,您可以:
- 理解表单验证的核心原理和设计模式
- 掌握常用验证规则的实现方法
- 构建健壮、可扩展的表单验证系统
- 提升用户体验和数据质量
附录:验证器工具函数完整代码
typescript
export interface ValidationResult {
valid: boolean;
message: string;
}
export interface PasswordStrength {
level: number;
label: string;
color: string;
}
export function isEmail(value: string): boolean {
const regex: RegExp = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(value);
}
export function isPhone(value: string): boolean {
const regex: RegExp = /^1[3-9]\d{9}$/;
return regex.test(value);
}
export function isIdCard(value: string): boolean {
const regex: RegExp = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/;
return regex.test(value);
}
export function isPasswordStrong(value: string): boolean {
if (value.length < 8) {
return false;
}
const hasUpper = /[A-Z]/.test(value);
const hasLower = /[a-z]/.test(value);
const hasNumber = /\d/.test(value);
const hasSpecial = /[!@#$%^&*()_+\-=\[\]{}|;:,.<>?]/.test(value);
return hasUpper && hasLower && (hasNumber || hasSpecial);
}
export function isNumber(value: string): boolean {
const regex: RegExp = /^-?\d+(\.\d+)?$/;
return regex.test(value);
}
export function validateEmail(value: string): ValidationResult {
if (!value) {
return { valid: false, message: '请输入邮箱地址' };
}
if (!isEmail(value)) {
return { valid: false, message: '请输入有效的邮箱地址,如: example@email.com' };
}
return { valid: true, message: '✓ 邮箱格式正确' };
}
export function validatePhone(value: string): ValidationResult {
if (!value) {
return { valid: false, message: '请输入手机号码' };
}
if (!isPhone(value)) {
return { valid: false, message: '请输入有效的11位手机号码' };
}
return { valid: true, message: '✓ 手机号码格式正确' };
}
export function validateIdCard(value: string): ValidationResult {
if (!value) {
return { valid: false, message: '请输入身份证号码' };
}
if (!isIdCard(value)) {
return { valid: false, message: '请输入有效的18位身份证号码' };
}
return { valid: true, message: '✓ 身份证号码格式正确' };
}
export function validatePassword(value: string): ValidationResult {
if (!value) {
return { valid: false, message: '请输入密码' };
}
if (value.length < 8) {
return { valid: false, message: '密码长度至少8位' };
}
if (!isPasswordStrong(value)) {
return { valid: false, message: '密码需要包含大小写字母和数字/特殊字符' };
}
return { valid: true, message: '✓ 密码强度符合要求' };
}
export function validateLengthRange(value: string, min: number, max: number): ValidationResult {
if (!value) {
return { valid: false, message: '请输入内容' };
}
if (value.length < min) {
return { valid: false, message: `内容长度不能少于${min}个字符` };
}
if (value.length > max) {
return { valid: false, message: `内容长度不能超过${max}个字符` };
}
return { valid: true, message: '✓ 长度符合要求' };
}
export function validateNumber(value: string): ValidationResult {
if (!value) {
return { valid: false, message: '请输入数字' };
}
if (!isNumber(value)) {
return { valid: false, message: '请输入有效的数字' };
}
return { valid: true, message: '✓ 数字格式正确' };
}
export function getPasswordStrength(value: string): PasswordStrength {
let level = 0;
if (value.length >= 6) level++;
if (value.length >= 10) level++;
if (/[a-z]/.test(value)) level++;
if (/[A-Z]/.test(value)) level++;
if (/\d/.test(value)) level++;
if (/[!@#$%^&*()_+\-=\[\]{}|;:,.<>?]/.test(value)) level++;
const result: PasswordStrength = { level: level, label: '', color: '' };
if (level <= 2) {
result.label = '弱';
result.color = '#FF4757';
} else if (level <= 4) {
result.label = '中';
result.color = '#FFA502';
} else {
result.label = '强';
result.color = '#2ED573';
}
return result;
}
---、