组件化的度

最近快被贵司的后台架构搞崩溃了,吐槽吐槽发泄一下。

加入贵司后,接手前端开发,主做后台产品。按理说,我在前司做了5年后台产品,轻车熟路,熟的不能再熟,这项工作对我来说应该再合适不过。结果进展非常不顺,甚至老板来敲打我,说工作效率太低,需求做的太慢。

我也不得不承认,就目前来看,自己确实没有达到很高的效率。不过我也得吐槽一下,这是有原因的!不是我不努力,而是现在的架构实在太太太……(我得选个好词,以免伤害到球哥的感情),太僵硬吧。

(目前的后台架构是球哥基于 Vue 设计开发的组件化框架。球哥是个码力十足,有追求有实力的人,我非常欣赏他,然则这套框架我实在不敢苟同。)

Vue 最大的优势,就在于它用很低的成本赋予了 Web MVVM 的能力。于是我们这些前端开发者可以用很低的软件复杂度,完成非常优秀的产品。而且,其作者还尽可能的把 Vue 解耦,降低入门门槛和使用门槛,我们不需要上来就面对一大批陌生的名词和概念,几乎可以直接上手实操。

可惜,与之同期到来的,还有“组件化”这把双刃剑。组件化当然有好处,它方便我们复用代码,提升效率。但也有很多开发者走上歪路——写组件库,用组件库。写组件库当然有好处,比如,是个扬名立万的好机会。用组件库也有好处,可以提升效率,省去很多开发量。

但是,所有组件库都有一个避不开的槛——颗粒度。颗粒度太细,用起来跟原生 HTML 差不多,意义不大;颗粒度太粗,一旦不合业务逻辑,就会很蛋疼(对,我就很蛋疼,简直每隔几天蛋就要碎一次……)。怎么样来确定颗粒度的粗细呢?只能看业务了。

2B 开发的特点

回到2B业务上。2B开发有几个明显不同于2C开发的地方:

  1. 业务流程又长又复杂
    • 2C 业务,比如微博、微信,用户操作简单直接,效果反馈很快
    • 2B 业务,如果某个操作要填10个表单,经过5个环节审批,那就一个都少不了
  2. 客户多付费,必须尊重他们的需求

这两点之下,会产生一些要求:

  1. 表单复杂,校验项多,灵活性要求高。此时,表单生成器就不再适用;只要,要把可以生成的简化版,和需要人工的复杂版分开。
  2. 表格多,列多,单元格多变。此时,表格组件也不宜复杂,处理好简单的分页筛选即可,剩下的还是交给业务相关的组件。
  3. 表格表单复用性差,很多表格表单都只出现一次,或者有自己独特的需求。此时,组件的优势不复存在,工作量怎么做都是接近的,反倒是原始的做法更清晰直接。

2B 开发的组件化

所以,我认为,2B 的产品,不适宜上来就用组件库搭建复杂的框架。相反,用最简单直接的 HTML、JS 元素,加上 MVVM 特性,效果会更好。

初期

最合适的组件化方案就是“不要过早组件化”。组件化的颗粒度以增强原始 HTML 元素为主,比如 Typeahead(输入搜索下拉选择)、给 Input 增加通用的校验工具,等等。用几乎最原始的方式开发,完成需求。这样看起来土,但带来的两个非常重要的好处:

  1. 思路直接,符合直觉,开发者不用投入大量精力学习新框架可以凭直觉完成
  2. 颗粒细,方便实现各种业务逻辑。比如表单想哪几个字段一起校验就一起校验,想怎么显示数据就怎么显示数据。

中期

这个时候我们应该完成很多需求了,哪些组件使用频繁值得抽象,哪些组件很复杂但是只用一次,大家心理都有数了,就可以开始着手进行组件化的工作。

不过,这个时候,仍然不需要做什么表单生成器、表格生成器之类的东西。我们要做的,是强业务相关组件。比如,某个表单在多处被重复使用,我们就要想办法把它抽象成组件(一般用到第3次,就要考虑做组件,用到第4次就一定要做成组件)。做出来的组件,只包含这个表单;抽象的目的:统一管理这个表单,方便别人复用。

抽象成组件的目的,是让今后的开发更成熟快捷,不是为了提炼出一套通用工具集,放到市场上扬名立万。

晚期

这个时候我们应该积累了不少组件,足以应付日常需求,可以快速响应快速开发。此时,组件化以重构为主。

最终,很遗憾,我们并未收获一套普适的通用组。但是,我们得到一套和业务深度绑定的组件库,和建筑于底层,弹性好鲁棒佳的架构。


这才是我眼中的组件化之道。

小试 Element UI

经过一个小项目的试用,我觉得 Element UI 1.2.5 设计上过分激进,质量上存在一些影响开发进度的 bug,所以不推荐使用。

不确定写多长,写先结论吧:暂时不推荐使用。原因如下:

  1. 影响使用的小 Bug 有点多
  2. 需要重新学习一门语言


接下来详述。

从前司离职之后,我开始更新技术栈。离开惯用的 Backbone,考虑再三,投入 Vue 怀抱。选择 Vue,而不是竞品 Angular、React 有三个理由:

  1. 文档友好,社区活跃。
  2. 模块拆分的很好,学习曲线平缓。
  3. 基于标准化技术,可以最大限度的避免浪费。

不过实操之后发现,Vue 与我惯用的 Bootstrap 有些冲突,主要在于:

  1. Bootstrap 对过渡效果和切换的操作依赖于样式,比如 .active.in。Vue 在处理模板时会把当前样式先缓存起来,然后根据数据增删绑定的样式。此时就可能出问题,tab 页切不动或者动画突然打断之类的。
  2. Bootstrap 会广播特定的事件,这些事件无法被 Vue 捕获,只能在 mounted() 的钩子里手工绑定。

于是我觉得,既然根基(jQuery)变了,最好把整条线都更新了吧。左右看了看,准备先试下 Element UI。这是饿了么推出的基于 Vue2.0 的组件库,目测组件齐全,文档详细,而且直接以 2.0 为基础,符合我追新的想法。

实际用了之后……唉……有点……遗憾。项目地址

首先,Element UI 把所有组件都封装了,包括布局,比如 <el-row><el-col>,我觉得这样太过了。从现实经验来看,布局元素几乎不可能够用,别人总要补充一些。封装的元素我不太知道最终生成的代码是什么样的,也就不好操作,总不能审查元素一个一个看吧?——对了,Element 的文档里缺少样式列表,也是个问题。

封装的另一个问题,所有元素都要通过后期渲染,总让我感觉不舒服。以及,我几乎无论干什么都要查文档,几乎没法直接动手,这和我选择 Vue 的初衷是相违背的。

接下来,小 Bug,有点多。除去布局和提示之类,我只用到3个组件:<el-button><el-table><el-pagination>,结果就遇到4个 bug,浪费很多时间去调试,有两个我给他们开了 issue,还有两个懒得弄了。这里列一下吧:

  1. <el-button :loading="scope.row.fetching"> 无法把 loading 绑定到数据的 .fetching 属性上
  2. <el-pagination> 设置 total 不更新视图
  3. <el-pagination> 更新 total 之后再次广播 current-change 事件,导致重复刷新
  4. <el-table> 里每行的 ref 属性没法正确生成数组

2017-04-04 更新:

经过 issue 沟通,我的理解的确有些问题,

  1. Vue 绑定属性的时候,如果该属性层次较深,比如 a.b.c,就不会修改它的 getter/setter,于是会失去响应式更新的特性;同时也不会报错。
  2. <el-table> 不是通过 v-for 渲染的,自然 ref 的表现也就正常了。

可能别的组件很健壮吧,我运气不好。

总之,我觉得就目前这个版本,1.2.5,来看,Element UI 还没到让人放心用并且用得好的程度。

下一次我可能会选别家的再试下,或者继续用 Bootstrap 然后自己拼些小组件出来——我这次就是想找个有 loading 的 button 才找 UI 库的。


啊,最后,还是感谢 Element UI 团队,感谢饿了么。希望你们再接再厉,相信将来这套库会更好。