前端组件的抽象复用思考

代码写多了,有点上进的人都知道要提取公共方法等做法,为的是让我们能够快速的实现需求。

抽象后的产物通常都是聚集着一些特定的共性,面向对象其实就是一种很好的抽象思维。

放到前端这边,我们就需要对日常的 UI 组件进行抽象,但 UI 组件有 ta 的特殊性,界面样式很难固化,潮流总是在变的,昨天拟物化,今天扁平化了。

抽象复用的利:

提高开发效率,信手拈来,copy + past,改一改数据项就可以使用。

抽象复用的弊:

难以面面俱到,总会有一些产物是游离在规则之外的,特别是视觉层面的东西不确定性太大,相比按钮组件的封装是改动很小的一种 UI 组件,而一旦涉及到多元素结合加样式的组件,局限性就暴露了,传统思维的抽象封装就很难 Hold 住。

例子🌰:

下面这张图是阿里云一个模块下的菜单项,父级有箭头图表,二级菜单可能有图标,也可能没有。

第一层级的抽象:

上来就撸的思路:预留图标,文字的配置项,这样到用的时候很方便,只要引入组件,然后设置配置项数据就可以复刻一个结构,功能一样的菜单。

有经验的会想:如果这是一个三级的菜单项,或者四级,五级,甚至是不知道多少级的菜单项,那么我需要抽象成递归树的形式,这样就涵盖了同类型的布局需求(都是树状,缩进型)。

但是还不够,哪天文字后面要加上一个 icon,或者你需要改动 DOM 结构,不光要放文字,还要图片了,此时的菜单组件就很鸡肋,根本不满足。

这种场景很容易出现在我们使用 UI 库开发的项目中,虽然我用了 Element-ui ,但是库提供的步骤条组件不太符合我的需求,我还想在两个步骤之间的线条上加一些文字。

你得怎么处理?去用定位的方式来 Hack 实现视觉稿么?不好维护还容易被吐槽!有追求的前端都是不愿这么搞的。

这也是我较为讨厌一方面使用了第三方 UI 库,另一方面又喜欢做一些定制化功能的项目,大多数的 UI 库是针对 PC 端的,目的就是为了统一并提高开发效率,既然你想定制了,干嘛还要用 UI 库呢,到最后的结果就是,乱糟糟的去覆盖 UI 库的组件样式,样式之间错综复杂,一不小心就弄成了全局覆盖,越到后面,越不可控。

第二级的抽象:

思路就是一种抽象!

上面的例子中使用递归树来实现当前导航菜单的方式就是一种思路体现。

数据结构 + 数据 + 样式 = 最终的页面效果

HTML 是砖,帮助我们实现数据结构,上面提到的递归思维是数据结构的体现,菜单内容或者图标 icon 是数据实体,CSS 则是最后的装饰。

知道了上面的思路后,回到复用,抽象上面,为什么要抽象复用?

这是我之前总结的对于提取公共组件的一些想法:

  1. 公共组件在短期内能被 5 个及以上的页面使用么?
  2. 拆分成公用组件是为了提高效率,能否保证组件的代码质量?配以使用文档,demo,快速入门?
  3. 我的公共组件能让别人在 5 分钟内不看代码只通过 ReadMe 快速入门运行起来么?
  4. 时间、复杂度、代码质量… 我的公共组件能解决什么问题?节约多少时间?节约多少人力?是否可以衡量?
  5. 如果回答不了上面 4 问,为什么还要写公共组件?

所以为了保证抽象后的复用性,同时兼顾组件的灵活,我采取的策略是:

  1. 组件应当用来服务于特定的业务项目。

如果业务中的菜单栏就是长成上面这样,那么我就可以简单的写死成两级的情况,稍微通用一点使用递归的方式做。

但前提是这个组件是有成为公共组件的需求存在,如果都不一定要成为公共组件,你不抽象不封装都没有问题,考虑到时间成本,没有复用性的组件,压根就没必要做抽象。

所以,「数据结构 + 数据 + 样式 = 最终的页面效果」 的思路下,对于菜单组件,我们要复用的是递归的结构,只要提供了这个框架,在这上层细节的布局就按照特定的业务形态去实现就好了,之后这个菜单组件就可以在这个业务中做复用。

代价:

这种思维下,只提供底层的结构框架,对于业务中的组件不做特定的抽象,那么每个开发者遇到相同的功能还是需要基于底层框架,重新写一遍代码,尤其是只有细微差别地方,也要写一部分重复的代码,是比较诟病的。

像上面的这种弹窗,底层的框架可能只有

  • 点击按钮后 Dialog 展示
  • 内容框(白色的部分)
  • 右上角关闭按钮。

如果业务不做抽象封装,那么每个开发就需要写一遍上述的弹窗,但是如果你需要修改内容的排版,变为居左展示,按钮要展示两个,title 不需要了,内容还要几张图片,此时就符合第二种抽象方式,快速引入一个基本的 Dialog 组件,基于这个框架自己写特定的内容部分,就相对比较灵活。

总结:

  1. 样式异变,潮流易变,“一劳永逸”对于 UI 组件来说很难。
  2. 当试图减少一些偷懒而保持易用性时,总会带来额外的成本,要多写一些代码,有些成本是省不掉的,只会以某种形式转移了。
  3. 抽象的粒度因实际项目的复用性程度而定。
  4. 做行为和结构的抽象,不做样式的抽象。