写此篇原因
前端组件设计思路或原则的,此类文章网上已很多,有些陈词滥调了。但此类文章大部分都是直接写结果而忽略了原因,导致阅读难度较大。本人希望从解决问题的角度出发,写这篇组件设计。如果觉得有点作用,希望大家帮忙点一下赞。
为什么要写组件?
此问题没有固定的答案,大家要根据自身情况来思考。我在当前项目,写公共组件原因是:核心是为了提高团队效率,其次是统一ui风格。
因为当前项目是管理后台项目,只有4,5个前端,且进入了中期。若是前期,写组件核心或许会放在维护性和统一ui风格上,使项目可以更稳定,组件也更容易迭代。但现在是中期,且前端人数不算多。重心放在提升团队效率上即可。
该怎么写公共组件?
根据我自身项目的情况,重心放在提升团队效率即可。那我要解决的问题有:
- 易上手,易阅读,一接触就能使用其核心功能,其余功能也能快速上手使用。
- 中高度封装,尽量减少使用者的代码量。
- 通用性强,能满足常见的业务功能
解决以上问题,那么我会得到一个高效率的公共组件。目前基础的增删改查的表格页面,三个组件即可搞定。
由于团队特殊情况,很多小伙伴不太愿意使用公共组件。易上手易阅读这个是我首要解决的问题。大家根据自身当前情况,来思考自己要解决什么问题。我给自己定的标准是:小组件,使用者一看就能直接使用。大组件,2分钟内就能直接上手使用。
🐯 易上手易阅读问题
解决这个问题,得思考使用者是怎么样使用组件的。第一,使用者如何查询到此组件。由于我们团队没有组件库,要使用某个组件,小伙伴会有两个途径去查询这个组件。1、看到之前在使用此组件的代码。2、直接看组件代码。第二,使用者,在使用组件中,如果遇到特殊的业务功能,他如何在组件中找到合适的api去解决此问题。
1.文档
由于团队无组件库文档,文档只能写在代码上。除了有此组件和api的说明,特定增加了一个快速复制粘贴功能。确保使用者看到此组件,马上就能使用此组件的核心功能。
2.单一职责
这个就是老生常谈的组件思路了。举个例子说明一下:我写了个页面表格组件。后面小伙伴说我这组件能否api加个选项,可以选择是否需要分页器。
我回答肯定是:NO。因为我这页面表格组件,它的职责就是:表格和分页器的连接器。如果你要单表格,不要分页器,那么你就用基础表格组件,而不是页面表格组件。如果在这个组件加了是否需要分页器的选项,把不该是它的事放到了它身上,看起来它的功能多了,但其实混淆了它的职责,影响阅读性。
- 组件源码
<template>
// 基础表格组件
<BaseTable
:data="props.data"
v-bind="$attrs"
:columns="columnsHandle"
:handleList="handleList"
@selection-change="selectionChange"
>
<template v-for="item in slotList" :key="item" #[item]="scope">
<slot :name="item" v-bind="scope"></slot>
</template>
</BaseTable>
// 分页器
<Pagination
v-model:page="pageNum"
v-model:limit="pageSize"
@pagination="paginationFnc"
:total="total"
/>
</template>
3.主次之分(单一职责的补充)
阅读性最高的组件,就是只有一个主要api,只要有这个api有参数这个组件就能运行,其他的api只是起辅助作用。这个是我自己悟的,仅供大家参考哈。举个例子:
// elementplaus的input组件
<el-input v-model="input" style="width: 240px" placeholder="Please input" />
这个组件核心api就是v-model,只要这个api有值,就完成了此组件80%的功能。其他的api仅起辅助作用。大家可以留意一下,elementplaus组件,基本都是如此。现在vue3主版本支撑defineModel,v-model使用更加容易了,善用多用v-model,会让你的组件可阅读性上一个档次。
但很多时候,组件需要两个api才能运行起来,有两个主要api,其实也行。比如我写的基础表格组件,需要date和columns两个属性才能跑起来。这种阅读性会差一点,但也还行。但主要api不能超过3,一个组件要3个主要api才能运行起来,那这个组件使用起来,肯定是苛刻,阅读性也差。
<template>
<BaseTable :data="data" :columns="columns">
</BaseTable>
</template>
<script lang="ts" setup>
const data = ref([
{
data1: 1,
data2: 2,
}])
const columns = ref([
{ label: 'data1表格头', prop: 'data1', showOverflowTooltip: true }
])
</script>
4.扁平化的state和props
给组件传递props时,建议用更扁平化的props,而不要用嵌套的对象或数组。这个懂得都懂。
特殊情况,需要传对象,比如我做的搜索栏组件,其中有一个参数itemProps传入的就是对象。但这个api职责其实也是单一,比如搜索框是input框,如果要修改这个input框,那就在itemProps传入ElInput的api即可。
这条原则,核心是每个api职责必须是单一的,别整混合api。
API columns = {
type:'input' | 'select' | 'datePicker'
itemProps:{} //element组件的内容,完美继承
label:搜索标题
key:搜索key
}
5.单向数据流
这个是vue或react任何框架的常规操作了,组件也应追寻此原则。之前有小伙伴就是父级将一整对象传到子组件里面,然后子组件直接对该对象进行操作,子组件直接操作父组件数据,数据逆流向,后期维护阅读性极差。看完他的组件,我马上就重构了。
6.添加ts类型
ts类型提示能大大减轻组件的使用难度,这是不容质疑的。react是个js库,天生有ts支持。vue虽然一直在加强对ts的支持,但还是有不少问题。这个只能尽量写。但如果是通过数据去驱动的中大型组件,那必须写组件的ts支持,能大大减轻使用者负担。
举个例子:
import { ElSelect } from 'element-plus'
type ElSelectPropsValue = InstanceType<typeof ElSelect>['$props']
export type Column =
| ({
type: 'select'
itemProps?: ElSelectPropsValue
optionList: optionListValue
})
上面是我组件api的一个ts类型。我在itemProps直接加上了ElSelect的ts类型。那么使用者在使用此api调整select框的时候,就会有ts的提示。能大大提升效率。
总结:
现在我们模拟使用一下目前的公共组件。遇到有一个要使用的组件,直接复制示例到使用的地方即可。这个公共组件职责是单一的,主要依靠一个api即可运行起来。一下子就能知道这个组件的用法了。如果需要适配,ts有语法支持。这个组件实在是太好用了。
以上当然是我臆想,现实中才没那么顺利(ಥ﹏ಥ)。但这一套组合拳下来,组件的可阅读性和上手难度,应该得到了大大的改善。大家如果有更好的组件设计思路,欢迎补充。
🐯通用性问题
如果一个组件易读易上手,往往这个代码通用性也是很强的。不过还有些得补充一下,先在这里挖一下坑,过几天再补上(如果公司不突然加活的话)。欢迎大佬指出问题和提供建议,感谢。如果觉得这篇文章有点用,帮忙点一下赞哈。
Tip 如何解决问题?三步走
1.发现问题。2.解决问题。3.监控问题
举个大家习以为常的例子,发现了某个模块在很多地方都要使用,写个组件封装一下,把ui和常用参数配置好,让大家只关注业务即可。上面两步就是发现问题和解决问题。
但很多人忽略了第三步,第三步其实是必需的。把组件搞完后,拿一段时间去看看小伙伴使用的情况。收集意见和修改。能让组件更丝滑,也能大大提升其他小伙伴使用组件的意愿。
原文链接:https://juejin.cn/post/7355285469640998939 作者:夏目友人爱吃豆腐