相信不少jym遇到过这种开发场景:项目都是表单表格,但是架不住页面量大,而且开发完架不住客户的变动频繁。这种场景一般都会基于二次封装公共组件及部分业务组件去解决,那么对于Form这一部分内容,怎么样能做到敏捷开发、易拓展迭代呢。
我们的开发场景针对于vue2或者2.7+antd的场景,如非此场景,逻辑也可以参考,只不过逻辑稍作修改。
先说下我们对表单的一个处理吧:
- 代码基于cli自动生成。(这部分我打算单独出篇文章详细说明下过程,基本就是通过二次封装表单结合脚手架根据模板文件生成对应表单的相关文件)
- 封装公共表单组件。(这个我说的是公共组件,不想和业务挂钩,目的是摆脱当前项目场景下依然能够使用。)
针对于表单的封装我们怎样处理的呢?
-
使用数组配置的数据源去渲染表单项,类似antd中表格的column配置,那么使用的时候应该关心哪些配置呢(首先是表单FormItem的属性配置,一般来说就是初始值initialValue和规则配置rules;其次就是针对表单渲染的组件的属性配置了)
如下是一个完整的表单配置:
-
既然组件是针对数组格式去渲染的,怎么监听属性值的变化或者一些组件方法的触发呢?其实antd的表单对组件的emit进行了拦截,只要是渲染的组件在change的时候emit了input和change,form都能拦截的到新值的变动。针对方法的处理,组件内部采用了vue中$listeners能够处理传过来的方法对象解决的;为了方便处理特殊逻辑,我们在配置项目添加了名为on的配置属性,在on中可以配置对于属性名的事件处理。
-
针对于有级联关系的表单项,我们怎么处理的呢?首先配置一定要定义成计算属性,这样表单项的配置可以根据外来因素控制项的隐藏和显示。为什么没有搞成添加属性api去处理,是因为防止之后类似的逻辑的出现,出现无穷尽api的迭代。
类似图中的配置,虚拟商品的时候才会显示这些配置项。
-
表单中如果想要插入特殊的内容,比如破折线,标题栏等怎么处理呢?类似antd中表格的columns一样,我们也提供了customRender的函数配置,在这个函数里可以类似插槽自定义插入任何内容。
-
既然表单的数据源是数组类型,可以在.vue文件中提出来吗?当然可以,如果已经定义成computed关联了this,也不用担心,在定义computed的时候使用bind绑定一下this指向就行。如果使用的vue2.7,推荐将依赖项弄成hooks进行存储。
-
封装的form表单怎么获取form实例呢?比如进行antd表单检验或者赋取值的时候都需要通过调用实例的函数去实现的,在使用组件的时候,我们提供一个onLoad函数,在函数内部可以获取到函数实例。
-
示例图片中没有出现各个渲染项的render属性,怎么处理的每一项渲染成什么组件呢?配置项中存在一个名为component属性的配置,这个属性有两种传值方式。第一种是传入一个全局注册过的组件名称,另外一种是将组件import后将变量传入。这两种写法都依赖于jsx的支持,所以vue-cli或者vite必须使用能支持jsx的版本或插件。
代码简化封装如下(仅限于v2相关版本,vue-cli使用支持jsx的版本)
<script>
export default {
name: "CustomForm",
props: [
"value",
"dataSource",
"num",
"disabled",
"viewabled",
] /* [数据源, 一行显示几个, 禁用折叠, 是否预览] */,
data() {
return {
form: null,
};
},
computed: {
span() {
let n = this.num || 4;
return parseInt(24 / n);
},
},
created() {
this.form = this.$form.createForm(this, {
name: "SForm_" + new Date().getTime(),
onValuesChange: (p, v) => { // 支持v-model
if (!this.value) return;
this.$emit("input", {
...this.form.getFieldsValue(),
...v,
});
},
});
},
mounted() {
this.$emit("onLoad", this.form);
},
render() {
return (
<a-form form={this.form} layout="horizontal">
<a-row gutter={24}>
{this.dataSource.map((d, i) => {
let Tag = d.component;
return (
<a-col key={d.key} span={d.span || this.span}>
{d.customRender ? (
d.customRender(d)
) : (
<a-formItem label={d.label} props={d.formItemProps}>
{this.form.getFieldDecorator(d.key, {
...(d.formProps || {}),
rules: [...((d.formProps && d.formProps.rules) || [])],
})(
<Tag
props={{
disabled: this.disabled,
...d.props,
}}
on={{ ...d.on }}
/>
)}
</a-formItem>
)}
</a-col>
);
})}
</a-row>
</a-form>
);
},
};
</script>
二次封装form核心代码如上,别看核心的代码量很小,但是2年内经过十多个项目的拷打。
第一次发文章,浏览排版比较难看,有时间会优化的。