前端组件化设计思路

目前前端三大框架(Vue.js, Angular.js, React.js)都在引领着前端的组件化开发方向,组件化的前端开发方式的确为业务实现带来了前所未有的方便,其实组件化开发早已经具有(YUI),但如何封装一个优秀的组件,可能并不是每位前端开发者都能够做好的。

组件封装有一定的不确定性,更多时候是在做几个方面的权衡,并且在业务不断变化中,可能还会面临一些调整和重构。

组件化开发的意义有很多,一些新手会狭隘地认为只是为了复用(包括对于模块化的理解),认为只有一个地方用就没必要抽取封装为组件,但实则不尽然:

  • 组件化是对实现的分层,是更有效地代码组合方式
  • 组件化是对资源的重组和优化,从而使项目资源管理更合理
  • 组件化有利于单元测试
  • 组件化对重构较友好

组件与模块

模块 (Module) 通常强调的是职责(分离、内聚),组件是可复用模块和相关依赖的封装。

组件可以如下定义:

  • 可复用的模块,完成既定功能
  • 有明确的接口规定
  • 有上下文依赖、外部依赖资源的定义
  • 可以独立发布

组件设计的原则

新手常常会更多地从实现(如所用的框架 Vue 的组件实现方式)直觉角度定义组件,并且隘于视角较局限,经验较欠缺,对于职责划分不合理。

以下原则尽可能使用,用得越多就组件的复用性就越好。

  1. 适用单一职责原则
  2. 适用开放封闭原则
  3. 追求短小精悍
  4. 避免太多参数
  5. 缩小信赖范围和向稳定方向信赖
  6. 适用 SPOT 法则 (Single Point Of Truth,就是尽量不要重复代码,出自《The Art of Unix Programming》)
  7. 追求无副作用
  8. 追求引用透明
  9. 避免暴露组件内部实现
  10. 避免直接操作 DOM
  11. 适用好莱坞法则 (好莱坞法则: Don’t call us, we’ll call you, 又称 IoC, Inversion of control, 控制反转)
  12. 入口处检查参数的有效性,出口处检查返回的正确性
  13. 充分隔离变化的部分
  14. 组件和数据分享,信赖一致性的数据结构

自省的几个问题

以上原则有点太教科书,不结合长期的实践深刻理解,很难灵活运用,所以我设计了以下几个自省问题,在思考一个组件时候,从这几个问题入手,引导完善组件的设计。

这个组件可否(有必要)再分?

组件划分的依据通常是 业务逻辑、功能,要考虑各组件之间的关系是否明确(如组件树方式管理组件间依赖关系,兄弟组件不可见),以及组件的可复用度。

划分粒度的大小需要根据实际情况权衡,太小会提升维护成本,太大又不够灵活和高复用性。

每一个组件都应该有其独特的划分目的的,有的是为了复用实现,有的是为了封装复杂度清晰业务实现。

这个组件的依赖是否可再缩减?

缩减组件依赖可以提高组件的可复用度,常用的方法是 IoC(依赖注入),对外弱类型依赖。

这个组件是否对其它组件造成侵入?

一个组件的封装性不够,或者自身越界操作,就可能对自身之外造成了侵入,这种情况应该尽量避免,确保组件的生命周期能够对其影响进行有效的管理(如 destroy 后不留痕迹)。

较常见的一种情况是:组件运行时对 window 对象添加 resize 监听事件以实现组件响应视窗尺寸变化事件,这种需求的更好替代方案是:组件提供刷新方法,由父组件实现调用(最终由根组件统一处理)。

次优的方案是,当组件 destroy 前清理恢复。

一个组件不应对其它兄弟组件造成直接影响。

这个组件可否复用于其它类似场景中?

需要考虑需要适用的不同场景,在组件接口设计时进行必要的兼容。

这个组件当别人用时,会怎么想?

接口设计符合规范和大众习惯,尽量让别人用起来简单易上手,易上手是指更符合直觉。

假如业务需要不需要这个功能,是否方便清除?

各组件之前以组合的关系互相配合,也是对功能需求的模块化抽象,当需求变化时可以将实现以模块粒度进行调整。

发表评论