文章类型: 原创阅读时长: 1小时
文章地址:
公共组件封装原则
1、单一职责原则:一个组件应该只负责一个功能
2、可复用性原则:组件应该是独立的通用的,可以在多种上下文使用
3、可配置性:组件设计时应该具有足够的配置选项和参数,用户可以根据参数控制组件行为
- props: 属性
- slots:插槽
- 事件:通知外部组件,比如点击、改变、成功、失败
- hooks:处理一些通用逻辑,然后注入到组件中,增强组件
- Provide/Inject:共享数据
- 全局配置:组件库级别的处理,比如颜色、字体、图标、语言等
4、可测试性 - 使用测试框架来测试组件,比如jest、vitest、jasmine、mocha等
- 模拟数据和事件,模拟数据来测试组件的行为,事件来测试组件的交互
5、单向数据流:数据从外部进入组件内部,组件内部不处理数据,数据变化出发事件通知父组件处理
表格组件封装历程
此次表格组件的核心是:实现单一职责原则
其他的封装原则之前都是严格遵守的,只是这个表格内部实现了对用户自定义列的获取、展示、控制等,这不符合单一职责原则,表格应该只负责表格列的控制和内容展示
收集表格逻辑
由于我对表格的封装、功能不熟悉,所以需要熟悉一下。
1、表格的列可以配置显示/隐藏,列的顺序可以拖拽
2、表格的列可以配置默认列(默认展示的列)、固定列(所有表格的列),相当于在初始化时,可以展示你想展示的那几列(默认列)
3、表格的列通过api获取 格式:['name', 'age', 'sex'],意义在于保存到后端后,刷新页面可以获取到这些列,通过返回的列的key从而展示这些列
4、表格列(columns)需要自行组装,不同表格的列的title、width、minWdth、sorter都是变化的,需要在不同组件内编写函数来组装
5、表格数据排序
6、一些表格除了固定列以外,还有用户自定义的列,也就是在用户设置里可以对可以定义表格自定义列的表格进行自定义列添加,后续可以在这条数据的编辑页面修改自定义列的值,表格就负责显示自定义列和值
之前封装的表格流程
1、封装了一个抽象类来获取表格列和用户自定义列(这是一种很好的设计模式 -- 模板方法模式,但是也不是很适用,因为这两个方法是有通用逻辑的,抽象方法里没有逻辑,所以每个页面都得重新写)
2、用户在具体页面继承抽象类,并且实现抽象类中的抽象方法,获取表格列和用户自定义列
3、在获取表格列和用户自定义列时,会组装列和用户自定义列(这是在组件外部组装的)
4、最后会把这些配置传给表格组件
5、表格组件内部会根据用户自定义的列配置,获取到列的值然后组装显示(用户自定义列就和表格耦合了,表格不应该处理自定义列的值这部分内容)
6、表格的列变化后,表格内部封装了一个重新组装列的方法对列进行重新组装(表格内部不应该组装列)
以上的流程,在封装表格组件时,会存在很多问题,都在后面括号里
我的思路
由于刚开始我不了解组件表格的逻辑,所以我只是想着把原来的逻辑通过 hooks + 组件的方式来封装,先保留原有表格组件的功能,然后再抽离公共逻辑
1、我将 获取表格列和用户自定义列 的方法写到了 hooks 里,并且整合了这过程中必要的逻辑,用户只需要提供 api 就可以获取到列了,列的组装函数也是通过参数提供的
2、我把逻辑全放到 hooks 里了,然后把需要用到的列的信息、自定义列的信息、自定义列的数据、列的操作等都返回出去了,表格只需要接受这些参数就可以了
3、项目里有25个页面使用到了表格,由于那两个抽象方法的原因,好多多余的逻辑在那两个方法里实现了,我在观察完这些表格后,对这两个方法进行了重构,把冗余的逻辑都删除了,但是也添加了很多当时感觉是必要的逻辑,导致方法过于庞大(好多都是列排序的逻辑,实际上是多余的,排序完全可以根据显示列的顺序来对表格传入的 columns 进行排序,排序逻辑应该在表格组件内部,或者是组装列时通过 computed 来实时排序,而不是每一次有变化我都要调一次排序函数,这里我就不得不吐槽一下原作者了,由于我也没认真思考,被带偏了)
我的问题
1、首先我的思路是在原有的基础上对整个组件进行实现,优化了用户可配置功能和减小了开发者使用难度,但是我并没有用重构的思路去解决这个问题,也就是我并没有解决当前组件表格和自定义列耦合的问题
2、我的思路被原组件的实现方式限制了,这个组件相当于还是原来的组件,就是代码换了一种写法
优化思路
leader 也是耐心地让我好好想想这个重构任务到底要做什么,我应该如何去分解这个任务
叫我先不用管自定义列,先实现表格的基础功能
1、实现表格列在组件外部的组装
2、实现表格列显示隐藏控制
3、实现表格列的排序功能
后续就是对自定义列的处理
(都是在组件外部处理的)
1、我写了一个自定义列的 hooks,通过 api 获取自定义列,然后组装列(涉及到后端api的,都应该传入 api 来调用(可配置、可复用)或者是暴露事件到组件外部处理(可扩展、可配置))
2、我编写了一个用于渲染自定义列的组件,和一个 hooks 处理用户自定义列数据
3、我将列组合搭配一起后传给表格组件
4、通过插槽来将自定义列组件放到自定义列的位置,并且把自定义列的数据传给自定义列组件,自定义列数据更新可以通过表格数据变化事件来触发更新
5、列的显示/隐藏、排序变化后重新生成列我不用考虑,因为全部的列是一开始就组装好了的,表格内部只是展示可以展示的那几列,排序我也可以根据后端返回的列进行排序,所以我完全去掉了重新组装列的逻辑(这个逻辑之前组件设计不好导致的)
more:列的组装其实挺繁琐的,因为宽度、展示列的信息等内容需要自定义,所以这个组装函数和表格的名称这些都有挺多耦合的,也是通过不同的页面传入不同的函数来解耦的。
到现在我就实现了 去掉用户自定义列和固定列的耦合,表格内部也是只处理了列的展示,完全不用担心列的组装,希望这类表格的封装能给你一定的思路启发(我陆陆续续干了差不多两个星期,哭死)
我的解释
可能你看完想不明白我为什么要花这么多时间去做这件事情,你先别急,听我慢慢道来:最大原因是这个组件不符合软件设计原则,虽然能用,但是如果自定义列有新的逻辑进来,开发者就得扩展这部分逻辑,到后面组件慢慢就变成了 shi 山,如果自定义列有新的渲染方式,那么组件内还得扩展自定义列那个组件,表格内部就不应该有其他与表格无关的组件,一个简单的表格控制功能,变成了充满了自定义列内容的天下。如果这个组件只是保留了基础的列显示/隐藏控制、列渲染、提供插槽功能,那么后续无论什么需求进来,表格都不用动,动的是 hooks,插槽内容,或者是具体页面内的逻辑(可能就一个页面特殊需要扩展,其他的都不用)。