如何开发公司组件库(上)

2,860 阅读7分钟

前端业务是依托于组件实现的,随着公司业务不断增多,从那些功能相似的业务中可以抽离出一些公共组件,形成公司的组件库。组件库的开发是一项复杂的工作,除需实现功能之外还要考虑构建、发布、测试等一系列因素。但一个经过良好设计的成熟组件库往往会在之后节省我们大量的时间,让我们能更快速高效的实现业务需求。这篇文章是我在开发公司组件库时的一些实践分享,希望能对大家有所帮助。本文基于 Angular 框架,React 和 VUE 基本思路大同小异。

在开发组件之前,我们需要做一些准备工作以确保所开发的组件能产生价值,包括:需求评审、调研同类型组件和发出组件issue。

需求评审

这部分的内容需要由提出组件抽离需求的成员来完成。

需求评审的意义在于确定组件开发的目的,没有经过需求评审就盲目开发的结果只能是浪费时间。需求评审需要确定以下几点内容:

  1. 哪些项目在使用以及组件需要具备的功能 这是组件开发的意义所在,也是组件设计的依据,必须要给出具体的项目与功能描述。
  2. 是否有必要抽离组件 组件开发一般耗时较长,要衡量利弊。一般优先抽取业务相关性较强的组件,比如标注器、关系图谱等或者非常通用能迅速节省开发时间的组件,比如权限模块、表格组件等等。
  3. 由谁负责组件开发和 Code Review 组件的开发工作必须通过代码审查,以保证组件库的质量。因此在确定组件开发人员的时候需要指定人员负责 Code Review, 建议组件库有固定的成员负责 Code Review。

充分调研同类型组件

日光之下无新事,所需开发的组件往往并不新鲜早已被人实现,我们只需要在此基础上做一些调整即可。对已有的成熟组件的调研是十分必要的,它可以帮助我们避免因为自负犯下的错误。调研主要包括:

  1. 组件的使用方式 对于 Angular 项目来讲,组件有多种表现形式可以提供功能。以ng-zorro-antd中的组件为例,nz-button通过指令(DIRECTIVE)的形式实现按钮相关功能,nz-message通过服务(SERVICE)来实现全局提示功能,nz-table以组件(COMPONENT)的形式实现功能,其内部又通过更细粒度的thtd组件与指令实现。可见不同用途的组件,其需要的使用方式也会有差异。调研的时候首先需要了解同类型的组件的使用方式。
  2. 组件的功能与对应 API 设计 这里需要注意的是 API 的命名,通常来讲某些功能的命名是约定俗成的,比如数据类 API 一般为*Data。参考规范的 API 命名,尽量减少标新立异,使 API 易于理解。
  3. 组件及功能实现方式 除了组件设计,还需要注意组件具体的实现方式。比如:组件类如何抽离依赖,组件的关键功能是如何实现的等等。

进行组件设计

组件的设计主要包含组件的使用方式和 API。在这一步需要输出一份组件的使用范例以及根据功能设计的组件 API 列表。在进行组件设计的时候,可能会遇到以下的问题,这是我的一些看法。

公共组件里要不要出现业务逻辑

组件是为了提供 UI 视图渲染。但是针对同样的 UI 视图渲染,不同的业务会引入不同的业务逻辑。比如用户列表组件与文章列表组件的 UI 视图渲染相同,但有着不同的业务逻辑(依赖的服务不同)。通常组件库不包含具体的业务逻辑,只封装组件自身 UI 逻辑与样式逻辑。那如果是公司的业务组件库,业务模式单一固定,为了方便快捷,是否应该将业务与组件 UI 逻辑耦合在组件内部呢?我的看法是,组件本身只应该承担视图渲染的作用,即便业务固定,也应该剥离业务逻辑去设计组件。固定的业务逻辑可以单独封装,没有必要与组件逻辑耦合。组件应该保持纯净。比如需要根据用户是否登录的状态下显示相应文字,我们应该提供一个可以只包含 UI 渲染(自定义显示文字)的组件而非一个包含业务逻辑(依赖于用户登录信息)的组件。如下所示:

// 同时包含业务逻辑(用户信息服务)与 UI 渲染的组件
@Component({
  selector: 'download-span',
  template: `
  <button [disabled]="disabled">{{disabled?'无法下载':'下载'}}</button>
  `
})
class  DownloadSpanComponent {
  constructor(private userService: UserService) {}
  get disabled(): boolean {
    return !userService.hasLogin()
  }
}
//  不包含业务逻辑只包含 UI 渲染的组件
@Component({
  selector: 'download-span'
  template: `
  <button [disabled]="disabled">{{disabled?'无法下载':'下载'}}</button>
  `
})
class DownloadSpanComponent {
  @Input()
  disabled: boolean;
}

如何去判断组件需要什么样的 API

建立在第一个问题的结论上,我们才能判断组件到底需要什么样的 API。首先组件的 API 必然是与组件的视图渲染相关的,比如 [loading]指定表格是否显示加载中视图,[data]指定表格中的每一个单元格中的具体内容。因此,API 的设计需要体现它所直接映射的功能。而且最好每一个 API 映射的功能是彼此独立的。其次,由于组件不包含业务逻辑,因此组件的 API 也应与业务无关。所以 API 不能带有具体的业务特征,比如不能添加一个[showUserId]用来表示用做用户列表时是否显示用户 ID列。最后,组件的设计绝不是为了符合所有的业务需求,也没有这个必要。因此,在设计 API 的时候应该考虑是否大多数场景都会需要用到这个 API。如果只是为了满足极少数的场景需求,那么就没有必要提供这个 API。因为很显然组件的功能越简单,组件的稳定性就会越好,拓展起来也越方便。特殊功能应该在具体的项目中进一步定制组件实现。

如何更新组件

组件的设计不是一劳永逸,需要不断添加新的功能或更改已有的功能。在更新组件的时候,除了需要遵循以上两点讨论的结果外,还有一些需要考虑。首先保证新功能不会对已有的功能产生破坏,这是显而易见的。其次,更新的功能不可以由已有的功能单独或组合实现。比如添加新功能[pagination]表示表格的分页状况,这个功能可以用[pageIndex][pageSize]组合表示。

根据组件设计提出 issue

组件的 issue 必须要包含以下内容:简介、使用示例、功能列表和 API。除此以外,可以针对性的添加一些其他信息,比如动机、开发背景、迁移策略等等。在提出 issue 之后需要和同事积极讨论,至少需要确定功能列表和 API 满足大家的使用需求。issue 内容大致确定之后,可以点击 gitlab 提供的 Create merge request 按钮,提出一个 WIP 状态的MR。然后在默认提供的分支上开发组件。因为组件开发往往包含的代码量很多,如果开发结束之后再提出 MR,会导致负责 Code Review 非常困难。所以提前发起 MR,让开发的过程始终可见,也方便大家在 MR 下随时讨论。

至此,组件开发的前期准备工作就完成了,接下来进入具体的组件开发环节。

相关链接: 如何开发公司组件库(下)