编者按:本文转载自zeka的掘金文章
自react、vue等数据驱动的框架流行以来,诞生了许多相关组件库,但这些组件库的编程模式基本都是大同小异,虽然这些年都在不断迭代,但除了功能更加丰满,长相更加好看之外,也基本上没有什么质的变化。在 material、antd 等垄断地位下,也很少有个人或公司去选择做组件库了,组件库也逐渐退出了各大会议论坛。但随着 react hooks、vue composition api 的推出,我想,或许组件库有了新的突破点。
从某种意义上来说,组件可以分为有状态组件和无状态组件(函数式组件),理想状态下,我们期望所有的组件都是无状态组件,但实际上,组件库提供的组件 90% 以上都是有状态组件,因为组件内部不得不要维护一套逻辑非常复杂的状态,这不仅让代码的易读性、可维护性降低,也增加了个性定制化组件的难度。antd 为了让用户能够自己定制化组件,底层提供了 rc-xxx 一系列更加原子化的组件,但依然会受限于 html 结构,导致一些场景无法满足。
如果说,我们能够将“状态“和 UI 彻底(理想状态)分开,组件都是无状态组件,只接收属性,负责展示,所有的状态逻辑交由一个独立的包(这里我们暂且叫他 use-hooks吧)去管理,会发生什么?
我不喜欢 ant design 的设计,你可以只使用 use-hooks,去写一套自己的无状态组件库
我害怕圣诞彩蛋,你可以只使用无状态组件库,去写一套自己的 use-hooks
react、vue 版本合并在一个包里不再是梦
ant design 将不仅是设计语言,还是组件库基础设施
当然,以上或许都是理想状态,但我们一直在往这个方向上努力。而且有了一定的成果,下面通过 Vue 的示例来进一步的体验。
Vue 3 最大的特性是 composition api,它提供了一种方式让我们可以更大限度的复用逻辑,也让我们在开发大型项目的时候,使得逻辑更加清晰,在我们开发 Ant Design Vue 2.0 的时候发现,除了逻辑复用,composition api 还能帮助我们做组件解耦。
在我们开发组件的时候一直尽可能的做到“高内聚、低耦合”,但是有些组件始终没能彻底解耦,尤其是 Form 表单组件,没错,流水的组件,铁打的 Form:
历史不再提,直接看现状:我几乎查看了现有的基于 Vue 的所有主流组件库,包括 vuetify、element、vant 等,它们的 form 表单组件基本都是一个套路,当然也包括 ant-design-vue,都是在 form 中使用 provide 提供表单上下文,在 form-item 中使用 inject 注入表单上下文,input / select 等组件通过某种约定(或发布订阅、或依赖注入、或被动劫持)来和 form、form-item产生“通信“。这几个组件在源码层面就产生了各种耦合,对于使用层面,也不例外,各个组件之间必须按照“严格“的 API 规范进行传递参数,校验逻辑和展示强绑定。还是举个例子更直观,产品&设计要实现如下图示的一个表单,所有的错误信息统一在一处显示:但是,组件库不支持,要自研,实现成本高,要延期,和历史设计规范不一致,改设计,总之各种借口和理由开始各方撕逼了。
为了解决这一问题,当然不是撕逼的问题,而是组件逻辑和展示耦合的问题,我们提出了表单校验中心化处理的方案 —— useForm,表单的校验状态完全在useForm中去处理,对于a-form、a-form-item,仅仅用来做表单布局和样式,不再做校验的逻辑,他们之间也不用通过各种方法进行通信。甚至可以不需要 a-form,你可以使用任何html标签替代,当然为了语义化,多写几个字符串也没啥毛病。上代码:
定义一个响应式的数据和表单域规则:
// 数据 const modelRef = reactive({ name: '', region: undefined, type: [], }); // 表单域规则 const rulesRef = reactive({ name: [ { required: true, message: 'Please input name', }, ], region: [ { required: true, message: 'Please select region', }, ], type: [ { required: true, message: 'Please select type', type: 'array', }, ], });
分别作为 useForm 的第一和第二参数,返回值中将包含响应式的校验结果信息以及其它的辅助方法:
const { resetFields, validate, validateInfos, mergeValidateInfo } = useForm(modelRef, rulesRef);
你可以将校验结果绑定到任意 a-form-item上,如:
<a-form-item v-bind="validateInfos.name"></a-form-item>
你不需要关心 validateInfos.name 里面都有什么值,当然如果你想做更加自定义化的UI,例如,你想将错误信息通过弹窗的形式展示:
if(validateInfos.name.validateStatus === 'error') { alert(validateInfos.name.help); }
而 mergeValidateInfo 只是我们根据常用业务场景提供了一个合并校验信息的辅助方法,就像我们上方截图所示的合并错误信息在表单底部统一展示,代码如下:
const errorInfos = computed(() => { return mergeValidateInfo(toArray(validateInfos)); });
<a-form-item class="error-infos" v-bind="errorInfos"> <a-button type="primary" @click="onSubmit"> Create </a-button> </a-form-item>
完整代码可以通过 ant design vue 官网(https://2x.antdv.com/components/form-cn/#components-form-demo-useform-merge-status-info)查看。useForm 的 API 或许繁琐了些,但我更想分享的是一种思想,而且 useForm 的api 现在还处在讨论阶段,如果你有好的建议可以通过 issue 和我们互动交流。
除了 useForm,众多 useXxxx 都在讨论开发中,如果你有兴趣,可以发送简历到 antdv@foxmail.com,加入我们,一起打造下一代组件库,当然。同时项目组也有"海量"实习生名额。
注意:默认投递公司是蚂蚁金服战略投资的公司 —— 校宝在线,如果你要投递的是蚂蚁金服体验技术部,在邮件中特别注明。
最后的最后,useXxxx 是一个新的仓库,点个 star 再走呗 https://github.com/vueComponent/use
❤️爱心三连击
1.看到这里了就点个在看支持下吧,你的「点赞,在看」是我创作的动力。
2.关注公众号程序员成长指北,回复「1」加入高级前端交流群!「在这里有好多 前端 开发者,会讨论 前端 Node 知识,互相学习」!
3.也可添加微信【ikoala520】,一起成长。
“在看转发”是最大的支持