在 React 大生态下, 一个比较成熟的前端团队, 都会面对一个问题: 如何提高团队的开发效率?
一个系统拥有大量的业务场景和业务代码, 相似的页面和代码层出不穷, 如何管理和抽象这些相似的代码和模块, 这肯定是诸多团队都会遇到的问题
不断的拷代码? 还是抽象成 UI 组件或业务组件? 显然后者更高效
那么现在就面临一个选择: 一是选择 React 生态中已有的组件库, 例如 antDesignMaterial-UI 等比较成熟的组件库; 二是团队再开发一套属于自己的组件库有赞前端团队选择了后者, 产出并开源了 Zent ,Zent 提供了一整套基础的 UI 组件以及常用的业务组件, 目前我们有 45+ 组件, 这些组件都已经在有赞的各类 PC 业务中广泛使用本文我们就来聊一聊如何开发一套优秀的 React 组件库以及一套完整组件库的构成
一选择开源? 还是自己造轮子?
React 大环境里面有很多优秀的 UI 组件库, 国内比较有名的 antDesign, 国外的 Material-UI, 都是比较稳定和优秀的组件库那么我们为什么还要自己去开发一套组件库呢? 原因大致如下:
有赞各个业务线 PC 产品有独立的设计规范, 包括但不限于组件样式交互模式
有赞微商城零售美业等 PC 产品的业务场景较为复杂, 需要深度定制某些通用的组件, 如 Design 和 SKU 组件
需要同时支撑有赞多个业务部门的 PC 产品
团队成员以开源的模式参与组件库的开发, 期间会有很多互相的讨论碰撞, 本身也是对团队的锻炼过程
二组件库构成
构建一个完整的组件库需要考虑:
组件设计思路
组件代码规范
组件开发流程
组件测试
组件维护 (包括 PR / issue 管理发包文档)
1. 组件设计思路
组件是对一些具有相同业务场景和交互模式代码的抽象, 组件库首先应该保证各个组件的视觉风格和交互规范保持一致, X 组件在 A 业务场景是一个交互, 在 B 业务场景是另一个 UI 风格, 这样就无法对 X 进行抽象, 极大的增加了组件的构建成本所以, 设计组件之初, 首先需要抽象和约定一套统一的视觉风格和交互规范
其次, 组件库的 props 定义需要具备足够的可扩展性, 而且组件内部完全受控, 保持组件具有统一的输入和输出, 让我们来看一个 Button 的例子
- // Button is a react component of Zent
- <Button type = "primary"className = "customer-classname"loading = {
- true
- }
- disabled = {
- false
- }
- size = "large"onClick = {
- this.handleClick
- }> {
- children
- } </Button>/
这是一个 Button 组件, 我们定义了很多标记状态的 props, 比如 type 表示 Button 的视觉风格, size 表示尺寸, disabled 禁用, loading 状态等, 这些状态在组件内部都不会维护 state, 所有的状态由传入的 props 来决定, 自定义 className 方便我们做样式自定义, children 方便我们自定义 Button 的显示内容
Button 甚至提供了 a 标签的功能, 只要在 Button 上传入 props:href
- // Button as <a>
- <Button
- type="primary"
- className="customer-classname"
- href="https://www.youzan.com"
- target="_blank"
- >
有赞首页
</Button>
我们需要做几个约定:
组件所有状态受控于 props
组件 children 支持自定义 Dom 结构
不要写死组件内部的 Dom 结构
2. 组件代码规范
有赞前端内部组件库, 使用的是开源 lint 工具 -- felint
felint 是一个集成了 eslintstylelintgit hook 的前端代码检查工具 felint 为你的项目做以下三件事:
初始化 eslint/stylelint 配置文件, 无论是 react 项目 vue 项目 es5 还是 es6 都提供了针对性的配置方案
安装 eslint/stylelint 及其依赖到当前项目的 node_modules 里
挂载 git 钩子, 在你提交代码时进行强制校验
具体使用可以参考官方 doc -- felint 文档地址
3. 组件开发流程
约定好组件的设计思路和代码规范以后, 接下来我们就可以参与开发组件了, 组件库的基本开发流程, 包括以下几点:
组件初始化
组件 Coding
组件 Demo
Zent 里面有一个组件初始化命令: yarn new-component, 这个命令完成了组件大部分初始化工作, 包括自动创建组件需要的目录和模版代码, 添加组件 js 和 CSS 代码然后, 我们就可以开始写组件代码, 代码风格和规范严格按照 lint 的规范编写, 如果不符合规范, 是不能提交代码的写完组件以后, 需要写组件 Demo 并运行, 方式是本地启动 server 来运行组件 Demo, 这个可以组件作为组件的调试工具
4. 组件测试
js 单元测试框架有很多, chaijestmochakarma 等等, Zent 组件库使用的是 jest + enzyme 的组合, 下面来看一个例子:
- // Button UI test
- import { mount } from 'enzyme';
- describe('Button', () => {
- it('Button UI test', () => {
- const wrapper = mount(<Button>OK</Button>);
- expect(wrapper.hasClass('zent-btn')).toBe(true);
- expect(wrapper.text()).to.equal('OK');
- });
- });
使用 jest 做 UI 测试有局限性, 只能测试基本的 dom 结构 和样式, 一些逻辑交互无法测到, 只能覆盖大部分的情况
yarn test 用来执行测试脚本, 测试结果会显示在终端
5. 组件维护
组件日常维护占整个组件库生命周期的很大一部分, 组件库做起来了以后, 组件功能后续会不断迭代, 也许是 bug fix, 也可能是 new feature, 这些组件的迭代我们通过 PR 和 issue 来管理, 同时, 我们需要管理好组件的 changelog
总的来说, 组件维护主要包括: PR / issue 的处理, 发包和管理 changelog
下面以 Zent 为例, 来介绍一下 PR 规范
PR 标题规则:[ bug fix / breaking change / new feature ] 组件名字: 修改内容描述
前面方括号用来区分 PR / issue 的类型: bug fix - 组件 bug 修复; breaking change - 不兼容的改动; new feature - 新功能
修改内容尽可能言简意赅, 总结 PR 的改动或者描述 issue
描述请用中文
组件名字请用英文, 首字母大写
PR 用来生成 changelog, 规范的 PR 有助于生成比较清晰的 changelog, 一目了然, 来看一下 Zent 的例子:
<img src="https://img.yzcdn.cn/public_f...; alt="zent-components"width="616"height="960" />
组件发包
组件发包只有拥有发包权限的人才能操作, Zent 是以组件库为单位发包的, yarn build 会将整个 Zent 的代码打包, 使用命令 yarn publish 发包, 在发包之前会跑组件测试, 只有测试通过以后才能发包
组件文档
一份好的 doc 是一个优秀组件库的标准, 良好的文档能够提升组件库的整体品质和好感度, 愿意花时间好好写 doc 的团队, 那么他们产出的组件库应该也不会差到哪去, 组件库文档维护也是组件库生命周期里重要的一环, 有时候你甚至需要做到中英文双语 doc
这里附上 Zent 组件库的 doc 地址: Zent
三小结
在本文中, 我们从组件的设计思路编码规范开发流程测试日常维护这五个方面阐述了如何构建一个 React 组件库, 并且以 Zent 为例讲述了有赞是如何做的, 任何一个组件库都需要的经过这个生命周期, 但我们需要思考的是: 如何营造一个良好的组件库生态环境? 我们需要想办法让更多的人参与其中, 共同作为组件库的维护者, 选择开源是为了给 React 生态环境做输出, 在前端组件化已经成为了既定事实的今天, 我们不需要重复的造轮子, 而是需要在组件化方面尝试新的突破, 脱离前端技术的束缚, 站在工程师的高度去抽象自己手头的代码组件化这条路上, 我们还有很多事情要做, Zent 只是一个开始
来源: https://segmentfault.com/a/1190000013938837