表单组件必须用data-schema驱动,通过JSON Schema描述字段元信息并映射UI控件,onChange需返回数组路径以支持深层更新,校验器应支持同步异步混合且可插拔,多表单需通过formId隔离状态,重置须还原schema初始值。

表单组件必须用 data-schema 驱动,而不是硬编码结构
硬写 <input type="email"> 或 <select> 标签会锁死渲染逻辑,后续加校验、联动、国际化就只能改 DOM 操作代码。用 JSON Schema 描述字段类型、约束、UI 提示等元信息,组件只负责解析和映射。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 每个表单组件接收统一的
schemaprop,例如:{ "type": "string", "format": "email", "title": "邮箱", "required": true } - 把
type映射到具体 UI 控件("string"→<input>,"boolean"→<checkbox>),避免在模板里写 if-else 判断 - 预留
ui:widget字段支持覆盖默认控件,比如{"ui:widget": "textarea"}强制用多行文本框 - 不要在 schema 里塞 HTML 片段或内联样式——那是渲染层该干的事
onChange 必须返回完整字段路径,不能只传 value
用户改了嵌套对象里的某个子字段(如 user.profile.phone),如果只触发 onChange("13800138000"),上层根本不知道该更新哪一层数据。必须带上下文路径,否则无法做深层 diff 和局部重渲染。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 回调函数签名统一为
onChange(value, path),其中path是数组形式,如["user", "profile", "phone"] - 内部用
lodash.set或原生Object.assign拼接更新,避免手动遍历嵌套 - 表单级
onChange应合并所有字段变更,但需保留各字段独立的 path,方便做脏检查或字段级错误定位 - 别用字符串路径(如
"user.profile.phone")——点号在 key 里出现时会解析错,数组最稳妥
校验逻辑必须可插拔,且支持同步 + 异步混合
内置的 required、minLength 够用,但真实场景常要查后端(如用户名是否已存在)、调第三方 API(如身份证号合法性)、甚至读本地 IndexedDB。同步校验阻塞渲染,异步校验又得管理 loading 状态和取消机制。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 校验器设计成函数数组:
validators: [(val) => ..., async (val) => { ... }],组件内部统一调度 - 每个 validator 返回
{ valid: boolean, message?: string },异步 validator 必须返回 Promise - 异步校验触发时机要可控:比如仅在失焦(
onBlur)时跑,输入中不触发;否则用户每敲一个字都发请求 - 用
AbortController取消前序未完成的异步校验,避免旧响应覆盖新输入结果
别在组件内维护全局状态,用 useFormContext 或 formId 隔离实例
多个表单同页时(如侧边栏编辑 + 主区列表),如果所有组件都读写同一个 useState 或 store,字段名一撞就互相污染。更糟的是,表单重置、提交、禁用状态全乱套。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 每个表单实例生成唯一
formId,所有子组件通过context或 props 接收该 ID,校验、提交、重置都绑定到它 - 避免在
Input组件内部调useFormState—— 它应该只负责渲染和触发事件,状态管理交给 Form 根组件 - 如果用 React,推荐
useFormContext+FormProvider模式;若跨框架(如 Vue + React 混合),就靠formId+ 自定义事件通信 - 表单重置不能只清空 input value,必须还原整个 schema 对应的初始值,包括隐藏字段、条件显示字段
真正难的不是写出能用的表单组件,而是当业务方提“这个下拉要加搜索”“那个日期要支持范围选择”“所有字段加 tooltip”时,你不用动核心渲染逻辑,只改 schema 或注入新 widget 就能跑通。扩展性卡点永远在数据流设计和边界隔离上,不在 UI 写得多漂亮。



















