资讯动态

前端验证组件复用失败的根源与分层设计模型

发布时间:2026/10/15 13:39:43 来源:尧图企业网站定制
1. 验证组件复用问题的本质不是写得不够好而是设计没对路“如何提高验证组件的垂直复用、水平复用和平台复用”——这句话在前端工程化实践中几乎每天都会出现在某位技术负责人晨会的待办清单里或是某次Code Review中被反复圈出的重构建议。但有意思的是绝大多数团队在听到这个问题时第一反应是去翻文档、查UI库源码、甚至重写一套“更通用”的校验器却很少有人停下来问一句我们到底在复用什么复用给谁用在什么上下文中才算真正“复用成功”我带过三个不同规模的中后台项目组从百人级SaaS平台到轻量级内部工具链发现一个高度一致的现象90%以上所谓“不可复用”的验证组件根本不是因为代码质量差或功能弱而是它们从诞生第一天起就被绑死在某个具体业务场景的语义里。比如一个叫PasswordStrengthValidator的组件表面看是做密码强度校验但它的规则硬编码了“必须含大小写字母数字特殊符号长度≥12”而它所在的项目恰好是金融类系统当另一个教育类项目想复用它时发现连“是否允许空格”这个基础配置都改不了——不是不能改是改完要动5个地方还要同步更新3份文档和2个测试用例。这已经不是技术问题是设计契约的失效。垂直复用、水平复用、平台复用这三个词听起来像术语堆砌其实对应着三类真实且高频的协作断点垂直复用指的是同一业务域内从表单页→弹窗→抽屉→卡片编辑等不同UI形态中验证逻辑能否无缝迁移水平复用指的是跨业务线之间如用户中心、订单管理、内容审核相同语义字段如手机号、邮箱、身份证号能否共享同一套验证定义与错误提示策略平台复用则是最底层的能力抽象——它不关心你是React还是Vue不关心你用Ant Design还是Element Plus甚至不关心你有没有UI框架只提供可插拔、可组合、可声明式调用的验证能力内核。这三个维度从来不是并列关系而是存在严格的依赖层级没有扎实的平台复用能力水平复用就是空中楼阁没有清晰的水平复用边界垂直复用就会陷入“每个页面都要魔改一遍”的泥潭。我见过太多团队把精力花在“怎么让一个React Hook在Vue里也能用”这种伪命题上却忽略了真正的瓶颈其实在于验证规则的定义方式本身就不支持解耦。比如用字符串写正则、用函数闭包存状态、用props传一堆布尔开关……这些写法在单页面开发中很顺手一旦进入多团队协同、多技术栈共存的环境就成了天然的复用屏障。所以这篇文章不讲“怎么封装一个高大上的Validator类”也不推某个明星开源库。我要带你回到验证这件事的起点重新理解“验证”在现代前端架构中的角色定位拆解它在不同复用维度下的真实约束条件并给出一套经过多个项目实测、无需强依赖特定框架、能直接落地的分层设计模型。你可以把它当成一份“验证组件复用自查清单”也可以当作一次面向真实协作场景的设计复盘——毕竟能被别人放心复用的代码从来不是写得最炫的而是让人一眼就懂“它承诺了什么又绝不做什么”的那一种。2. 复用失效的三大根源语义污染、上下文绑架、契约模糊为什么我们写了那么多验证逻辑却总在下一个项目里重头再来不是技术不行是我们在关键节点上做了错误的抽象选择。我把复用失败的根源归为三类每一种都对应着具体可识别、可修复的设计缺陷。2.1 语义污染把业务规则写进了组件名和参数名里这是最隐蔽也最普遍的问题。我们习惯用“业务语言”命名组件和API比如// ❌ 典型语义污染示例 const IDCardValidator ({ isForRealNameAuth, // 业务场景标识 needBackSideScan, // 物理操作要求 allowTemporaryID // 政策例外项 }) { ... }这段代码看似灵活实则已将“实名认证流程”“证件扫描环节”“临时身份证政策”全部耦合进验证器的接口契约中。当另一个不需要实名认证的项目想复用它时面对isForRealNameAuthfalse这种参数开发者第一反应不是“哦关掉就行”而是“关掉之后它还校验身份证格式吗错误文案会不会变成‘请完成实名认证’这种业务话术”提示验证组件的命名和参数应严格限定在数据本体层面。正确做法是分离“数据规范”与“业务策略”IDCardFormatValidator只负责格式校验15/18位、校验码、地区码有效性而isForRealNameAuth这类开关应由上层业务逻辑决定是否启用该验证器或组合其他验证器如RealNamePolicyValidator共同构成完整校验链。我曾在某政务系统重构中处理过类似案例原系统有7个不同名称的“手机号验证组件”分别叫MobileForLogin、MobileForRegister、MobileForSMSBinding……实际对比发现6个完全一致只有1个加了“不允许虚拟运营商号”的额外规则。最终我们只保留一个PhoneNumberValidator通过options: { allowVirtualOperators: boolean }控制差异所有调用方统一导入复用率从32%提升至97%。2.2 上下文绑架把UI渲染、状态管理、异步交互全塞进一个模块很多团队所谓的“验证组件”其实是“验证提示禁用按钮请求防抖错误上报”的超级聚合体。比如一个EmailInputWithValidation组件内部既处理onBlur触发校验又管理loading状态还内置了debounce(300)甚至直接调用api.checkEmailExists()。这种写法在单页应用初期效率很高但一旦需要复用问题立刻暴露水平复用时另一个项目可能用的是GraphQL而非RESTAPI路径和响应结构完全不同垂直复用时弹窗场景要求校验失败后聚焦输入框而卡片编辑场景要求静默提示不打断操作流平台复用时纯JS项目根本无法直接使用这个React组件。注意验证的核心职责只有一个——接收输入值返回校验结果通过/不通过 错误信息。UI渲染、状态流转、副作用触发必须由使用者按需组合。就像厨房里的刀它只负责切割至于切完是炒菜还是摆盘那是厨师的事。我们后来推行的“三明治验证模型”就基于此原则底层是纯函数式validate(value, options)中间层是useValidation(value, rules)这样的Hook仅管理校验状态与触发时机最上层才是ValidatedInput这类UI组件。三者完全解耦项目A可以只用底层函数做服务端校验项目B用Hook配合自定义UI项目C直接套用现成组件——这才是真正的分层复用。2.3 契约模糊没有明确定义“什么是验证通过”也没有约定错误信息的结构这是导致水平复用失败的终极原因。当两个团队各自实现PasswordValidator时他们对“验证通过”的理解可能天差地别团队空值处理错误信息类型异步校验失败返回Anull视为通过字符串密码太短Promise.reject(网络错误)B视为不通过对象{ code: TOO_SHORT, message: 至少6位 }Promise.resolve({ valid: false, error: { code: EXISTS } })这种契约不一致使得任何跨团队复用都变成一场灾难。你无法安全地import { validate } from shared/validators因为调用方永远不知道返回值是布尔值、对象还是Promise更不知道错误码体系是否兼容。我们最终在平台层强制推行了标准化验证契约Standard Validation Contract, SVC它规定输入value: any, options?: Recordstring, unknown同步返回{ valid: boolean; errors?: Array{ code: string; message: string; field?: string } }异步返回Promise{ valid: boolean; errors?: ... }所有内置规则邮箱、手机号、身份证等必须遵循同一错误码前缀规范如EMAIL_INVALID,MOBILE_FORMAT_ERROR这套契约不强制技术实现只约束接口形状。React团队用Hook实现Vue团队用Composable实现Node.js服务端用纯函数实现——只要返回结构一致上层业务代码就能无感切换。某次跨部门协作中A团队用Vue写的用户注册页直接集成了B团队用React维护的验证规则包零适配成本只因双方都遵守了SVC。3. 分层复用模型从平台内核到垂直封装的四级架构要系统性解决复用问题不能靠零散技巧必须建立一套可演进、可验证、可度量的分层架构。我们实践验证过的四级模型如下每一级都有明确的职责边界、交付物形态和复用范围且下层为上层提供能力支撑上层不得向下层注入业务逻辑。3.1 L1平台验证内核Platform Core——所有复用的基石这是整个复用体系的地基必须满足三个刚性要求无框架依赖、纯函数式、契约标准化。它不提供任何UI不管理任何状态只做一件事给定数据和规则返回结构化校验结果。我们采用TypeScript ESM模块构建核心导出如下// platform/validator-core export interface ValidationResult { valid: boolean; errors?: Array{ code: string; // 统一错误码如 REQUIRED, EMAIL_FORMAT message: string; // 本地化友好提示默认中文 field?: string; // 关联字段名用于批量校验定位 meta?: Recordstring, unknown; // 透传元数据供上层扩展 }; } export type ValidateFn (value: any, options?: Recordstring, unknown) ValidationResult | PromiseValidationResult; // 内置基础规则全部纯函数无副作用 export const required: ValidateFn (value) ({ valid: value ! null value ! value ! undefined, errors: value null || value || value undefined ? [{ code: REQUIRED, message: 此项为必填项 }] : undefined }); export const email: ValidateFn (value) { const regex /^[^\s][^\s]\.[^\s]$/; return { valid: typeof value string regex.test(value), errors: typeof value string !regex.test(value) ? [{ code: EMAIL_FORMAT, message: 请输入有效的邮箱地址 }] : undefined }; }; // 组合规则工厂关键解决复杂场景复用 export const compose (...fns: ValidateFn[]): ValidateFn { return (value, options) { const results fns.map(fn fn(value, options)); const allValid results.every(r r.valid); const allErrors results.flatMap(r r.errors || []); return { valid: allValid, errors: allValid ? undefined : allErrors }; }; };实操心得L1层严禁出现任何useState、useEffect、document、fetch等环境相关API。我们曾因一个console.warn调试日志被误提交到L1导致某Node.js服务端项目构建失败——因为服务端环境没有console对象。现在所有L1代码都通过Jest跑浏览器Node双环境测试确保100%同构。这个内核的价值在于它让“验证”回归本质。当你需要校验一个手机号时不再纠结“用哪个UI组件”而是直接调用mobile(value)当你需要组合“非空格式正确不在黑名单中”时只需compose(required, mobile, notInBlacklist)。规则即函数组合即编程这才是平台级复用的正确打开方式。3.2 L2框架适配层Framework Adapters——连接内核与UI生态L1是通用能力L2是桥梁。它的唯一使命是将L1的纯函数转化为各主流框架React/Vue/Svelte开发者熟悉的、符合其心智模型的API形态。这里的关键是“适配”而非“重写”。以React为例我们提供useValidationHook// platform/validator-react import { ValidateFn, ValidationResult } from platform/validator-core; export function useValidation( value: any, rules: ValidateFn | ValidateFn[], options?: { trigger?: onChange | onBlur | manual; debounce?: number; } ) { const [result, setResult] useStateValidationResult({ valid: true }); const [pending, setPending] useState(false); const validate useCallback(async () { setPending(true); try { const ruleList Array.isArray(rules) ? rules : [rules]; const promises ruleList.map(rule Promise.resolve(rule(value, options)).catch(e ({ valid: false, errors: [{ code: VALIDATION_ERROR, message: e.message || 校验异常 }] })) ); const results await Promise.all(promises); const allValid results.every(r r.valid); const allErrors results.flatMap(r r.errors || []); setResult({ valid: allValid, errors: allValid ? undefined : allErrors }); } finally { setPending(false); } }, [value, rules, options]); // 根据trigger自动绑定事件此处省略具体实现 useEffect(() { if (options?.trigger onChange) validate(); }, [value, validate, options?.trigger]); return { ...result, pending, validate, reset: () setResult({ valid: true }) }; }Vue版本则提供useValidationComposable// platform/validator-vue import { ref, onMounted, watch } from vue; import { ValidateFn, ValidationResult } from platform/validator-core; export function useValidation( value: Refany, rules: ValidateFn | ValidateFn[], options?: { trigger?: change | blur | manual } ) { const result refValidationResult({ valid: true }); const pending ref(false); const validate async () { pending.value true; try { // 同React版逻辑调用L1规则 const ruleList Array.isArray(rules) ? rules : [rules]; const results await Promise.all(ruleList.map(r r(value.value, options))); // ...合并结果 result.value { valid: allValid, errors: allValid ? undefined : allErrors }; } finally { pending.value false; } }; if (options?.trigger change) { watch(value, validate); } return { ...toRefs(result.value), pending: toRef(pending), validate, reset: () result.value { valid: true } }; }注意L2层代码量极少核心逻辑全部委托给L1。它的价值在于消除了框架心智负担——React开发者不用学新概念直接const { valid, errors } useValidation(...)Vue开发者同样const { valid, errors } useValidation(...)。我们刻意保持API签名高度一致降低跨框架学习成本。某次技术分享中一位Vue开发者现场用React版文档成功接入Vue项目只因API几乎一样。3.3 L3业务规则包Domain Rule Packages——水平复用的载体L1和L2解决了“怎么验证”L3解决“验证什么”。它是按业务领域组织的、可独立发布的NPM包如domain/identity-validators身份信息、domain/payment-validators支付信息、domain/content-validators内容安全。每个包包含领域专属规则如idCard、bankCard、contentSafeLevel领域错误码字典IDCARD_INVALID,BANKCARD_CHECKSUM_ERROR领域级组合规则如realNameAuthRules compose(idCard, name, mobile)多语言提示映射表供L1调用时自动注入关键设计点在于所有规则必须可配置、可覆盖、可组合。例如idCard规则// domain/identity-validators import { ValidateFn } from platform/validator-core; export const idCard: (options?: { allow15Digit?: boolean; requireCheckDigit?: boolean; customRegions?: string[]; }) ValidateFn (options {}) { return (value) { // 实现细节省略 if (!isValid) { return { valid: false, errors: [{ code: IDCARD_FORMAT, message: options.allow15Digit ? 请输入15位或18位身份证号 : 请输入18位身份证号 }] }; } return { valid: true }; }; };实操心得L3包必须通过L1的标准化契约测试。我们用Jest为每个包编写“契约合规性快照测试”确保idCard({ allow15Digit: true })(11010119900307299X)返回的结构与L1定义完全一致。这保证了任何L3包都能被任意L2适配器无缝消费。某次审计发现一个外包团队提交的domain/finance-validators包返回了{ success: true }而非{ valid: true }CI直接拦截强制整改——契约不是文档是代码级红线。3.4 L4垂直UI组件Vertical UI Components——面向具体场景的开箱即用方案这是离业务最近的一层也是用户感知最直接的部分。它不追求通用而是追求“开箱即用”针对表单页、弹窗、抽屉等典型场景预集成L1-L3能力提供即插即用的UI组件。例如ValidatedFormInputReact版// ui/validated-input import { useValidation } from platform/validator-react; import { idCard, mobile } from domain/identity-validators; interface ValidatedFormInputProps { name: string; label: string; value: string; onChange: (v: string) void; // 规则可传函数、数组、或预设key rules?: idCard | mobile | ValidateFn | ValidateFn[]; // 或直接传配置对象由组件内部映射 ruleConfig?: { type: idCard | mobile; options?: Recordstring, unknown; }; } export function ValidatedFormInput({ name, label, value, onChange, rules, ruleConfig }: ValidatedFormInputProps) { // 自动解析规则字符串key - L3规则函数 const resolveRules () { if (typeof rules string) { switch (rules) { case idCard: return idCard(); case mobile: return mobile(); default: return () ({ valid: true }); } } return Array.isArray(rules) ? rules : [rules]; }; const { valid, errors, pending } useValidation( value, resolveRules(), { trigger: onChange } ); return ( div classNameform-item label{label}/label input name{name} value{value} onChange{(e) onChange(e.target.value)} disabled{pending} / {pending span classNameloading校验中.../span} {!valid errors ( ul classNameerror-list {errors.map((err, i) ( li key{i} classNameerror-item{err.message}/li ))} /ul )} /div ); }提示L4层组件必须提供“降级通道”。比如ValidatedFormInput支持直接传入ValidateFn此时它就退化为一个纯验证增强的输入框不依赖任何预设规则。这保证了当业务有特殊需求时开发者不必撕掉重写只需替换规则函数即可。我们曾用这种方式在3小时内为一个紧急上线的海外项目替换了全部手机号规则为国际号码格式E.164零修改UI组件。4. 实操落地从零搭建可复用验证体系的7个关键步骤理论模型再完美不落地就是空中楼阁。下面是我带团队从零开始搭建这套复用体系的真实路径每一步都标注了耗时、风险点和避坑指南。整个过程在标准中后台项目中平均耗时12人日含测试与文档但后续所有新项目接入时间缩短至0.5人日以内。4.1 步骤1梳理现有验证资产建立“复用热力图”不要一上来就写代码。先花1-2天把当前项目中所有验证逻辑包括正则、函数、组件、Hook全部拉出来按三个维度打标签维度选项示例复用潜力高跨项目通用/ 中跨模块通用/ 低单页面专用email→ 高loginPasswordStrength→ 中couponCodeLength→ 低技术栈绑定无 / React / Vue / Node.jsuseFormValidation→ ReactvalidateIdCard→ 无业务耦合度无 / 轻仅字段名/ 重含业务流程required→ 无mobileForSMS→ 轻realNameAuthStep2→ 重然后生成一张热力图可用Excel或Miro横轴是业务模块纵轴是验证类型颜色深浅代表复用频率。我们会发现真正高频复用的往往只是20%的基础规则邮箱、手机号、非空、数字范围而80%的“定制规则”其实只在1-2个场景出现。避坑指南跳过这一步直接开发90%概率会造出一个“看起来很通用实则没人用”的大而全验证库。我们曾在一个项目中花3天开发了包含37个规则的shared/validators上线后发现只有email和required被复用其余35个从未被import过。根源就是没做热力分析凭感觉选题。4.2 步骤2定义并发布L1平台内核platform/validator-core这是最关键的一步耗时约2人日。核心动作创建新仓库初始化TypeScript项目配置ESM输出实现ValidationResult接口和ValidateFn类型编写5个最基础规则required、email、mobile、numberRange、maxLength全部纯函数无副作用实现compose组合工厂编写双环境测试Jest jsdom node-test-runner覆盖同步/异步、空值、边界值发布为0.1.0版本私有NPM Registry。实操心得L1的API必须极简。我们最初设计了createValidator(options)工厂函数后来删掉了——因为required就是一个函数email就是一个函数compose(a,b)就是一个函数再多一层抽象反而增加理解成本。记住L1的目标是让初中级开发者也能看懂、敢修改、愿复用。4.3 步骤3开发L2框架适配器React/Vue双端根据团队技术栈选择优先实现的适配器。React版约1.5人日Vue版约1人日。重点useValidation必须支持triggeronChange/onBlur/manual和debounce返回值结构与L1的ValidationResult严格一致valid,errors,pending提供reset方法方便表单重置所有副作用如setTimeout、fetch必须可被测试桩mock。注意不要试图在L2里做“智能判断”。比如有团队想让useValidation自动检测value类型如果是string就走同步Promise就走异步——这违反了契约一致性。L2只做环境适配不做逻辑决策。4.4 步骤4提取首个L3业务规则包domain/identity-validators选一个复用潜力最高的领域切入。身份信息手机号、邮箱、身份证、姓名是最佳选择因为几乎所有项目都需要。耗时约2人日创建新包依赖L1实现mobile支持中国国际号码、idCard15/18位、校验码、name中英文、长度为每个规则提供options配置项如mobile({ countryCode: CN })编写错误码字典和多语言提示映射发布0.1.0。避坑指南L3包的package.json中peerDependencies必须声明对L1的精确版本依赖如platform/validator-core: ^0.1.0避免消费者安装不兼容版本。我们曾因未声明peerDep导致一个项目同时装了L1的0.1.0和0.2.0引发运行时错误。4.5 步骤5开发L4垂直UI组件ValidatedFormInput选择最常用的表单输入场景。耗时约1.5人日基于L2的useValidation和L3的规则封装ValidatedFormInput支持rules传入字符串自动映射、函数、数组提供loading态、错误列表、禁用态样式所有样式用CSS-in-JSEmotion或纯CSS类不绑定UI库编写Storybook示例覆盖各种规则组合。实操心得L4组件的Props设计要“向后兼容”。比如rules参数我们同时支持mobile字符串映射、(v) {...}函数、[mobile, required]数组这样老项目升级时只需改一行rulesmobile无需重构整个调用链。4.6 步骤6在试点项目中灰度接入收集反馈选一个非核心、迭代快的内部工具作为试点。耗时0.5人日替换1-2个表单项为ValidatedFormInput用L3的mobile规则替代原有正则监控控制台报错、用户反馈、校验准确率重点观察错误提示是否自然加载态是否合理组合规则是否生效注意灰度期必须设置“逃生舱口”。我们在L4组件中预留了fallback属性当传入fallback{(value) legacyValidate(value)}时自动回退到旧逻辑。这让我们敢于快速试错某次发现idCard规则对港澳居民来往内地通行证支持不足立即切回旧逻辑2小时内补丁上线。4.7 步骤7编写开发者文档与迁移指南启动跨团队推广最后一步也是最容易被忽视的一步。耗时1人日编写《验证复用体系入门》5分钟看懂四级模型《迁移指南》如何将旧项目验证逻辑逐步迁移到新体系《规则开发指南》如何为L3包贡献新规则在内部Wiki建立“验证规则超市”列出所有可用规则、参数、错误码、示例组织一次1小时分享用试点项目数据说话如“复用率提升至92%表单校验Bug下降76%”。避坑指南文档必须包含“反模式”章节。我们专门列出常见错误写法❌const validator new Validator({ rules: [...] })违反纯函数原则❌rules{[mobile, (v) v.length 8]}内联函数无法被Tree Shaking❌message: 密码太短未使用L3的错误码字典导致国际化失败 这些比正确示例更能帮助开发者避开深坑。5. 常见问题与排查技巧实录那些踩过的坑比教程更有价值再完美的设计落地时也会遇到千奇百怪的问题。以下是我在多个项目中记录的真实问题、排查思路和解决方法按发生频率排序附赠独家排查技巧。5.1 问题1异步校验结果滞后用户已提交表单错误提示才弹出现象用户点击“提交”按钮表单瞬间提交成功2秒后ValidatedFormInput才显示“手机号已被注册”。用户困惑“我明明看到没报错才点的”。根因分析useValidation默认triggeronChange但提交时未主动触发校验。异步校验如api.checkMobileExists()需要显式调用validate()。解决方案在表单onSubmit中先调用所有字段的validate()等待全部Promise resolve后再提交或使用L1的validateAll工具函数我们后来补充的// platform/validator-core export const validateAll async ( fields: Array{ value: any; rules: ValidateFn | ValidateFn[] } ): PromiseArrayValidationResult { return Promise.all( fields.map(({ value, rules }) { const ruleList Array.isArray(rules) ? rules : [rules]; return Promise.all(ruleList.map(r r(value))).then(results { const allValid results.every(r r.valid); const allErrors results.flatMap(r r.errors || []); return { valid: allValid, errors: allValid ? undefined : allErrors }; }); }) ); };独家技巧在ValidatedFormInput中增加autoValidateOnSubmit属性当为true时组件内部监听父表单的submit事件自动触发校验。这样业务方只需ValidatedFormInput autoValidateOnSubmit /无需侵入表单逻辑。5.2 问题2L3规则包更新后老项目构建失败报错“找不到模块”现象domain/identity-validators发布1.2.0新增hkIdCard规则。某老项目npm install后构建报错Cannot find module domain/identity-validators。根因分析老项目package.json中写死了dependencies: { domain/identity-validators: 1.1.0 }未启用^或~。更深层原因是团队未建立“语义化版本管理规范”1.2.0本应是兼容性更新但实际引入了破坏性变更如重命名了idCard为chineseIdCard。解决方案强制推行SemVerL3包的1.x.x必须保持向后兼容破坏性变更升2.0.0在CI中加入“版本兼容性检查”每次PR自动对比package.json的exports字段与上一版检测是否有导出函数删除或重命名为L3包提供legacy入口如import { idCard as chineseIdCard } from domain/identity-validators/legacy平滑过渡。避坑指南我们后来在L1的compose函数中增加了debug模式compose.debug([a,b,c])会在控制台打印每一步的输入输出极大加速异步规则链调试。这个小功能帮我们定位了70%的规则组合问题。5.3 问题3多语言环境下错误提示仍是中文未随系统语言切换现象用户切换系统语言为英文ValidatedFormInput的错误提示仍是“请输入有效的邮箱地址”。根因分析L1规则函数中message是硬编码字符串未接入i18n系统。L3包虽提供了错误码但未与项目i18n资源绑定。解决方案L1规则函数的message参数改为可选当传入i18n实例时自动翻译export const email: (i18n?: I18nInstance) ValidateFn (i18n) { return (value) { const msg i18n ? i18n.t(EMAIL_FORMAT) : 请输入有效的邮箱地址; // ... }; };L3包导出messages对象供项目i18n系统导入// domain/identity-validators/messages/en.json { IDCARD_FORMAT: Invalid ID card number, MOBILE_FORMAT: Invalid mobile number }独家技巧在L4组件中我们支持i18nProp当传入时自动将所有L1/L3的message替换为i18n.t(code)。这样业务方只需ValidatedFormInput i18n{i18n} /零配置实现多语言。5.4 问题4表单重置后验证状态未清空仍显示红色错误提示现象用户填写表单后校验失败点击“重置”输入框清空了

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑