阁下,您的表单校验规则还维护的动吗?
表单校验是前端项目广泛存在的一个功能,因为Ant Design的引入,所谓的表单校验功能其实已经被抽象成了一个又一个的表单校验规则。以前并未注意到表单校验规则的可维护性,哪个组件用到,就在组件内完成即可。直到PM问了我2个问题:
“我们的账号名字符数限制区间是多少?”
“项目中所有涉及命名的字符数都要限制在3-26个,啥时候能改好?”
“好的”,我习惯性的打开了编辑器全局搜索。。。
后来的日子以上对话又重复了好几轮,每次内容略有不同啊!终于无法忍受的我开始动手对这部分内容专门做了重构,经过几个版本的迭代,似乎已经找到了一个好的方案将前端项目中的众多表单校验规则维护得体,便记下此文与各位前端大佬分享探讨。
影响维护性的几个问题
- 校验规则靠近并耦合业务组件,散落在项目各处
- 校验规则的复用
- 校验规则难以理解和可读
- 需要传参的校验规则
- 异步校验的规则
可维护的方案
该方案将所有的表单校验规则整理在
src/formRules
目录下统一管理,带来的收益是明显的:统一管理本身解决了问题1;./rules.ts & ./rulesHooks.tsx
作为所有业务组件消费校验规则的统一输出口,针对了问题1、2;./baseRuleCreator.tsx
针对了问题4;./baseSemiRule.tsx & ./utils.ts
针对了问题5;问题3么写注释就好了!
我们来看具体的内容:
./baseRules.tsx
// 必填的
export const requiredRule: RuleObject = {
required: true,
}
// 输入必须以字母开头
export const startLetterRule: RuleObject = {
pattern: /^[a-zA-Z]/,
message: 'Must start with a letter',
}
可以看到我们将表单校验规则拆成了原子级别,相同的规则我们只会写1遍,意外的收益在于每个校验报错信息也将是精准的。
./rules.ts
// Email
export const emailRules: RuleObject[] = [requiredRule, emailPatternRule]
// 密码
export const passwordRules: RuleObject[] = [requiredRule, length8_32Rule, passwordBanRule]
我们在该文件下自由组合复用原子化的校验规则,并导出给业务组件消费,达到了复用的目的。如果业务校验规则有修改,我们也可以仅在该文件统一做修改,完成了业务逻辑与校验规则的解耦。
./baseRuleCreator.tsx
// 校验是否与目标值匹配
export function createMatchTargetValueRule(targetValue: any): RuleObject {
return {
message: 'Does not match',
validator(rule: Rule, value: string) {
return value === targetValue ? Promise.resolve() : Promise.reject()
},
}
}
很明显该文件放的规则都是需要有入参的,不再赘述。
./baseSemiRule.tsx
// 异步校验邮箱是否重复
export const duplicatingAccountEmailSemiRule: TSemiRule = {
message: 'Email already exists',
callbackValidator: (value, resolve, reject) => {
return fetch(value).then(res => (res.data.exists ? reject() : resolve(undefined)))
},
}
显然该文件放了需要网络异步校验的规则,但为什么被命名为Semi
?难道阁下忘了防抖?虽然我们还需要一个防抖函数来统一加工这些校验规则才可实用。但非常棒的地方在于,这些规则在逻辑上已经自洽了,报错信息和校验逻辑都已经完整了。所以我们并不关心防抖函数的具体实现和校验规则在业务组件中的具体应用,如有修改则我们在这里维护即可,做到了关注点的分离。
./utils.ts
export function createDebounceRule(semiRule: TSemiRule): RuleObject {
const { message, callbackValidator, delayTime = 500 } = semiRule
let timeId: NodeJS.Timeout = null
return {
message,
validator(rule, value) {
return new Promise((resolve, reject) => {
clearTimeout(timeId)
timeId = setTimeout(() => {
callbackValidator(value, resolve, reject)
}, delayTime)
})
},
}
}
防抖函数的具体实现,不再赘述。
./rulesHooks.tsx
export function useVpcConnectionNameRules(target: string) {
const rules = useMemo(() => [
requiredRule,
startLetterRule,
createMatchTargetValueRule(target),
createDebounceRule(duplicatingAccountEmailSemiRule),
], [target])
return rules
}
需要入参的校验规则和异步校验规则利用hooks做抽象,实现复用。
千里之堤溃于蚁穴,勿以事小而不为!表单校验规则虽然是代码维护性一个鲜有前端boy关注的微小领域,但如果我们能关注到项目中一砖一瓦的维护性,则可以相信整个项目大厦也将是健壮无比的。
以上方案仅个人在项目中的实践,维护性的提升无可止境,各位前端大佬如有更好的方案,希望留言不吝赐教!
来源:juejin.cn/post/7259638617546637349