最近在公司参与实现一套类似ant design功能的组件库。我在项目中主要负责Menu,Tab,Progress, Input,Radio,Checkbox,Select以及Form组件的开发。与其他样式组件不同,Form组件除了显示表单项状态(loading,成功,失败,警告)等以外,还需要对校验逻辑进行封装,提高表单开发效率。功能定位与ant design依赖的rc-menu一致。但个人认为rc-menu的api过于复杂。
设计思路
表单组件根据职责可以拆分为三部分:校验逻辑执行、表单数据管理、表单样式显示,因此表单组件由三部分组成:
validator.js
提供与DOM无关的校验逻辑执行对象。职责为接受校验规则对象与表单数据对象,返回执行结果(即错误信息)。支持异步和多项联合校验。ValidationField
和formCreator
。formCreator
为高阶组件。formCreator
为被包装的组件管理表单数据,错误信息数据以及校验方法,并通过props和context与子组件通信。ValidationField
与formCreator
配合使用,ValidationField
接受props或者context的数据和方法将子组件(input、checkbox等)与formCreator的数据绑定。FormItem
(名字待定) 负责表单布局和表单输入组件状态的展示。
通过将表单分为这三部分,我们的组件有了更好的灵活性,如果不打算使用我们提供的校验方案,完全可以单独使用FormItem。
在组件的实现中,利用context的地方较多。之前由于了解不够,一直对react的context有所‘恐惧’,认为context是危险的(确实应该慎重使用context)。但经过一段时间的研究,发现合理的使用context是能够大量减少工作量,使得表单组件可以以更少的api覆盖绝大多数情况。有关context的问题请参考这里这里。在这儿还要说明一下,context最重要的问题是在pureComponent下,如果props或者state不变,那么context不能使组件更新。这个问题对于我来说不是问题,整个组件库全部未使用pureComponent或其他类似逻辑。而且context的使用全部由开发人员掌握,不会要求使用者对context处理(唯一的要求就是validationField
与FormCreator返回的组件之间不要有purecomponent组件包裹)。此外使用context的组件均提供了对应的prop作为替代选项。
这里还需要说明组件库不使用pureComponent的原因,使用pureComponent优化性能在实际项目中很有必要,但是作为基础组件库,使用pureComponent会带来浅比较的成本,所以这里有个tradeoff。如果使用者真的需要pureComponent的话,完全可以在我们提供的组件上在包装一层。‘过早优化是万恶之源’这句话我感觉适用于这里的情况。
示例代码
1 | import React, { PureComponent } from 'react'; |