JavaScript元编程实战:构建下一代动态表单验证引擎
一、架构设计
基于Proxy和装饰器的声明式表单验证系统,验证逻辑代码减少70%,支持复杂异步校验
二、核心实现
1. 响应式验证代理
// validationProxy.js
function createValidationProxy(formObj, rules) {
const errors = {};
const handler = {
set(target, prop, value) {
const validators = rules[prop] || [];
// 同步验证
errors[prop] = validators
.map(validate => validate(value))
.filter(Boolean);
target[prop] = value;
return true;
}
};
const proxy = new Proxy(formObj, handler);
return { proxy, errors };
}
// 使用示例
const { proxy, errors } = createValidationProxy(
{ username: '', password: '' },
{
username: [
val => val.length >= 6 || '至少6个字符',
val => /^[a-z0-9]+$/i.test(val) || '只能包含字母数字'
],
password: [
val => val.length >= 8 || '至少8个字符'
]
}
);
2. 异步验证装饰器
// asyncValidator.js
function asyncValidator(fn, message) {
return async function(value) {
try {
const isValid = await fn(value);
return isValid ? null : message;
} catch {
return '验证服务不可用';
}
};
}
// 使用示例
const rules = {
email: [
asyncValidator(
async email => {
const res = await fetch(`/check-email?email=${email}`);
return res.json().available;
},
'邮箱已被注册'
)
]
};
三、高级特性
1. 跨字段验证
// crossFieldValidator.js
function createCrossFieldValidator(getForm, validate) {
return function() {
const form = getForm();
return validate(form) || null;
};
}
// 使用示例
const rules = {
password: [
val => val.length >= 8 || '至少8个字符'
],
confirmPassword: [
createCrossFieldValidator(
() => proxy,
form => form.password === form.confirmPassword || '密码不匹配'
)
]
};
2. 动态规则引擎
// dynamicRules.js
function createDynamicRules(baseRules, condition) {
return new Proxy(baseRules, {
get(target, prop) {
const rules = target[prop] || [];
return condition(prop)
? [...rules, ...(target['*'] || [])]
: rules;
}
});
}
// 使用示例
const dynamicRules = createDynamicRules(
{
username: [val => !!val || '必填字段'],
'*': [val => !/script/i.test(val) || '禁止脚本输入']
},
field => field !== 'hiddenField'
);
四、完整案例
// formComponent.js
class ValidatedForm {
constructor(formId, rules) {
this.form = document.getElementById(formId);
this.initialValues = Object.fromEntries(
new FormData(this.form).entries()
);
const { proxy, errors } = createValidationProxy(
this.initialValues,
rules
);
this.proxy = proxy;
this.errors = errors;
this.form.addEventListener('input', e => {
this.proxy[e.target.name] = e.target.value;
this.updateErrors();
});
}
updateErrors() {
Object.entries(this.errors).forEach(([field, errors]) => {
const errorEl = this.form.querySelector(`.${field}-error`);
errorEl.textContent = errors[0] || '';
});
}
async validate() {
return Object.values(this.errors)
.every(errors => errors.length === 0);
}
}
// 初始化表单
const form = new ValidatedForm('signup-form', {
username: [
val => val.length >= 6 || '至少6个字符'
],
email: [
asyncValidator(checkEmail, '邮箱已被注册')
]
});