故事从何而起
注意:本文为寒草🌿的自我尝试,部分思想还是很稚嫩。大佬勿喷,不妨多多指教,一起交流~
大家好,我是小寒草🌿呀!
为什么是小寒草呢⚡️,因为我最近发现自己很渺小,真就是寄蜉蝣于天地,渺沧海之一粟。哀吾生之须臾,羡长江之无穷...
打住,我也不往远了扯,直接说事情的起源。在去年的年中,我的上一任荣誉小组长为我分享了他的组件拆分与设计经验,我也从他的身上学到了很多☀️
前几天在和一个实习生同学一起开发项目,我就借鉴了前荣誉小组长的经验,做了比较充分的前期设计工作。但是其实在开发完成后的 review
的过程中还是发现会存在一定程度的差异。
最开始我的组件设计把组件分成了四类:
这里的划分方案单纯的从我的经验出发,略显稚嫩。
- page 组件:一般不复用,即页面对应的组件,我眼里它相当于主板把各个组件和样式组合为一整个页面
- Layout 组件:纯样式复用,不包含业务
- 业务基础组件:参考
element-ui
等基础组件库,此类组件一般叫做xxx-form
,xxx-table
,暴露的api
也会基本与基础组件一致 - 基础业务组件:常见的业务基本都是由增删改查组成的,此类的组件也是对这一粒度业务的封装。此类组件一般叫做:
xxx-creator
,xxx-deleter
,xxx-updater
,xxx-viewer
之后我本次的例子也是来自于业务基础组件:
component TicketForm [ 工单信息表单 ticket-form --props-- disabled { Boolean } disabledFields { Array<String> } --events-- --methods-- getData() => TicketData; setData(ticketData: TicketData) => Undefined; clearData() => Undefined; validate() => Promise.<Boolean>; clearValidate() => Undefined; --slots-- ]
正如大家所见,我明确规定了一个业务基础组件 ticket-form
提供的 api
及所需的 props
props:
- disabled
- disabledFileds
methods:
- getData
- setData
- clearData
- validate
- clearValidate
之后就进入了开发阶段,但是在我进行 code review
的时候发现了 api
实现不完整的情况。起初我的强迫症犯了,但是后来我想了又想不等单纯的通过 review
去约束,这样成本极高,而且一致的组件其实并不具备很高的 review
价值。
我就去思考解决这个问题的办法,之后经过我大概五分钟的思考,就有了后面的故事。
故事又将如何延续
由于我的强迫症,我迫切的想要去解决这个问题,而这个问题其实被我定义为:
提供自动化方案,解决通用组件实现不一致的问题
首先,需要确定我们现阶段有什么:
- 公司内部基础组件库(各位参考 elementui)
- 系统内已实现的业务组件
- 我和前任小组长敲定下的组件设计规范(且不论这个规范是否优秀)
于是我画了这样一张图
下面我来对这个图进行一些解读:
- 首先我们通过团队的开发经验输出了一定的规范或者约束
- 之后,基于基础组件库或者业务组件库我们会产出一定的最佳实践或者 demo 案例
- 在之后经过一定阶段的博弈,大家对规范形成了一定的共识
- 对于我和前任小组长,我们形成了基础业务组件和业务基础组件的分类并将其 API 统一化
- 最终,基础组件足够统一话后我们就可以采用 low code 的方式进行输出
没错,最后我的想法就是用 low code
解决。大家可能会质疑这个代码怎么可能如此一致,但是就像上一章中 form
的案例,其实按照这种约束,代码还是及其统一的。
之后我花了一个小时,简单的做了一个生成 xxx-form 组件的 demo,其中生成代码的方法如下:
export const getFormCode = (schema) => { return `<template> <q-form ref="form" :model="formData" :rules="rules" :disabled="disabled" > ${ schema.map(item => ` <q-form-item label="${item.label}" prop="${item.key}" ${item.isInline?'class="inline-form-item"':''}> <q-input v-model="formData.${item.key}" :placeholder="请输入${item.label}" :disabled="disabledFields.includes('${item.key}')" /> </q-form-item> `).join('\n') } </q-form> </template> <script> const getBaseCustomFormData = () => ({ ${ schema.map(item => ` ${item.key}: '' `).join(',\n') } }) export default { props: { disabled: { type: Boolean, default: false }, // Array.<String> disabledFields: { type: Array, default: () => [] } }, data() { return { formData: getBaseCustomFormData(), rules: { ${ schema.filter(item => item.required).map(item => ` ${item.key}: [ { required: true, message: '请输入${item.label}', trigger: 'blur' } ] `).join(',\n') } }, }; }, methods: { setData(data) { this.formData = Object.assign(getBaseCustomFormData(), data); }, getData() { return Object.assign({}, this.formData); }, clearData() { this.formData = getBaseCustomFormData(); }, validate() { return this.$refs.form.validate(); }, clearValidate () { this.$refs.form.clearValidate(); }, } }; </script> <style lang="scss" scoped> .inline-form-item { display: inline-block; width: 50%; vertical-align: top; } </style> ` }
效果也比较简单,支持我们常用的布局配置:
最后生成的代码也一定是符合我对于 xxx-form
组件的规范的。
之后我拿着这个 demo 和我的前任小组长聊天,之后又在下班路上和大领导聊了聊组件设计和团队规范。
于是就有了最后一章的总结 ✨
最后一章留给总结
这只是一个简单的 demo,主要是和大家分享我在开发中的灵感一现,但是现在来看其实是有问题的:
- 关于组件分类与设计的合理性
- 关于这个代码生成程序的局限性
- ...
其实如果想做一个东西,应该有更多的准备,站在一个更大的视野去挖掘价值。本文思想比较稚嫩,也请多多指教。