构建组件库 / 工具库平台
背景介绍
基于小鱼前端现状,没有一个通用的组件和函数库平台,各个业务线在独自维护开发自己平台的组件和通用函数工具库,导致开发效率较低,没有办法统一管理组件和工具函数。
基于此,2021 年,我们考虑搭建自己的 PC 组件平台和通用工具函数库平台,方便各个业务能够快速开发和使用通用的组件和函数,避免无效的开发。
现已完成搭建的两个平台:
- XY-Element
- Toolkit
XY-Element (组件库)
现在的组件库主要针对 web 。
现状
现在已经推进并完成的相关工作:
- 文档管理与发布
- storybook 开发预览组件
- 组件打包与部署
- 生成组件模板
- 单元测试
TODO
- 组件库主题配置
- 组件 UI 和交互规范
- 基础组件开发
构架
- 文档:dumi
- 打包:rollup
- 组件管理:storybook
- 测试:jest
- 开发语言:typescript
开发指南
- 从 github 拉取 xy-element 项目:xy-element
bash
git clone git@github.com:xylink-com/xy-element.git
1
- 初始化项目:安装依赖
bash
yarn init
1
- 生成目标组件
bash
yarn generate ComponentNameWithCamelCase
1
或者使用:
bash
yarn gen ComponentNameWithCamelCase
1
生成组件目录如下:
txt
Scroller
├── demo 引入文档的 demo 目录
├────── basic.tsx 默认的文档 demo 示例
├── Scroller.scss 组件样式文件
├── Scroller.stories.tsx 组件 demo 或者案例文件
├── Scroller.test.tsx 组件测试文件
├── Scroller.tsx 组件主体文件
├── Scroller.types.ts 组件类型文件
├── index.en-US.md 组件英文文档
├── index.zh-CN.md 组件中文文档
└── index.tsx 组件入口,导出相关组件和接口
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
- 启动 storybook 开发组件
bash
yarn storybook
1
或者使用:
bash
yarn story
1
打开浏览器: http://localhost:6006
- 编写组件 demo
在 *.stories.tsx 中编写 demo,并且在 demo 目录中以默认导出的方式导出。 如在 demo 目录中 basic.tsx:
TS
/**
* title: Basic Usage // 案例英文标题
* desc: Set interval, reset interval and clear interval via setInterval. // 案例英文描述
*
* title.zh-CN: 基础用法 // 案例中文标题
* desc.zh-CN: 通过 setInterval 实现的setInterval, Reset 和 Clear 定时器的用法。 // 案例中文描述
*/
// 从 storybook 中导入组件
import { SimpleContainerWithBar } from '../ContainerWithBar.stories';
// 将组件实例默认导出
export default () => <SimpleContainerWithBar />;
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
storybook 允许一个组件有多个 demo, 只要在 story.tsx 文件中 export 多个 demo 实例就可以。
demo 案例可以在文档中引入,如:
html
<code src="./demo/basic.tsx" />
1
或者也可以直接在文档中以 tsx/jsx 的方式引入代码块渲染为 demo。
更多文档的配置项参见:TSX/JSX 支持的 FrontMatter 配置项
- 编写文档
在 index.en-US.md 中编写英文文档,在 index.zh-CN.md 中编写中文文档。 文档头部的配置如下:
txt
---
title: ContainerWithBar 组件名称
nav:
title: Components // 组件在页面顶部导航栏的归属
path: /components // 组件在页面顶部导航栏的路由
group:
title: Scroll // 组件在左侧导航栏的分组
path: /scroll // 组件在左侧导航栏分组的路由
legacy: /scroll/container-with-bar // 组件的路由
---
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
更多文档参考项请参考:Markdown 支持的 FrontMatter 配置项
- 预览开发文档
bash
yarn start
1
打开浏览器: http://localhost:8000
- 测试组件库
bash
yarn test
1
或者:
bash
yarn test:watch
1
- 打包组件库
bash
yarn build
1
或者:
bash
yarn b
1
- 打包组件文档
bash
yarn build:doc
1
或者:
bash
yarn bd
1
快速打包组件库和静态文档:
bash
yarn go
1
- 发布组件库
bash
yarn pub
1
或者:
bash
yarn p
1
- 发布静态文档
bash
yarn pub:doc
1
或者:
bash
yarn pd
1
Q&A
- 生成的模板文件的详细使用方法 使用
yarn gen ExampleComponent
生成的文件如下:
- ExampleComponent.tsx 这里写组件的主要逻辑,会相应的生成组件的模板代码,如:
ts
import React from "react";
import ExampleComponentProps from "./ExampleComponent.types";
import "./ExampleComponent.scss";
const ExampleComponent: React.FC<ExampleComponentProps> = (props) => {
const {...restProps} = props;
return (
<div data-testid="ExampleComponent" {...restProps} className="" />
)
};
ExampleComponent.displayName = "ExampleComponent";
export default ExampleComponent;
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
可以专注于写组件的业务逻辑。
- ExampleComponent.scss
这里写组件样式,主题文件会自动引入。
ts
@import "src/theme.scss";
1
- ExampleComponent.types.ts
这里写组件的类型、接口等。
ts
export default interface ExampleComponentProps {
}
1
2
2
- ExampleComponent.stories.tsx
这里是组件 demo ,可以打开 storybook 看到实时的开发效果。这里会导出一个默认的 demo 。
ts
import React from "react";
import ExampleComponent from "./ExampleComponent";
export default {
title: "ExampleComponent" // storybook 侧边导航栏标题
};
export const BasicExampleComponent = () => <ExampleComponent />;
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
- ExampleComponent.test.tsx 这里是测试文件,可以使用 jest 和 testing-library 进行单元测试。
ts
import React from "react";
import { render } from "@testing-library/react";
import ExampleComponent from "./ExampleComponent";
import ExampleComponentProps from "./ExampleComponent.types";
1
2
3
4
5
2
3
4
5
这里是中英文文档,生成的文件中已经有默认的配置,写文档时只要稍作修改即可。
md
---
title: ExampleComponent
nav:
title: Components
path: /components
group:
title: Example
path: /example
legacy: /example/component-name
---
# ExampleComponent
// 组件简介
## 示例
<code src="./demo/basic.tsx" />
## 用法
\`\`\`ts
\`\`\`
### 参数
| 参数 | 描述 | 类型 | 默认值 |
| ---- | ---- | ---- | ------ |
### 接口与类型
\`\`\`ts
\`\`\`
### 工作流
#### TOFIX
#### TODO
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
默认文档中包含了实例、用法、参数、接口与类型、TODO 等项,可以介绍清楚组件的使用方法、案例和开发计划。
- index.tsx
这个文件会帮你导出组件、组件类型,总结组件需要向外暴露的部分。
ts
import ExampleComponent from "./ExampleComponent";
import ExampleComponentProps from "./ExampleComponent.types";
export { ExampleComponentProps };
export default ExampleComponent;
1
2
3
4
5
2
3
4
5
- 组件和文档分别是怎样部署的?
- 组件由 master 分支维护,分版本发布到 npm。发布方式为手动发布。
- 文档的更新与发布:
- 目前文档的发布方式:文档原件保存在 guide 目录,打包后在 docs 目录。打包文档后执行
yarn pd
,发布到 github-pages。 - 计划的发布方式:开发人员不再关注文档打包和部署,开发者将分支合并至 master 后,由 GitHub action 打包项目文档,并发布到 gh-pages 分支作为静态文档发布。发布方式为自动发布,这样开发者只需关注文档编写即可。
- 目前文档的发布方式:文档原件保存在 guide 目录,打包后在 docs 目录。打包文档后执行
- 为什么要同时使用 dumi 和 storybook?
- storybook 开发组件很方便,但是生成静态文档不是很方便。
- dumi 只作为文档工具来使用,即我们写的文档最后会被自动打包和发布,开发中最常使用的仍然是 storybook。
- 后期我们可以将 storybook 的组件文档也进行发布,用户既可以在 storybook 中浏览组件列表,也可以在 dumi 中看到组件的使用方法和开发动向。
- 开发中只启用 storybook 即可,编写文档时可以本地打开 dumi 查看文档效果。
怎么更改组件模板? 组件模板可以在 utils/templates 目录中进行修改,可以新增模板文件或者修改现有的模板。
storybook 中开发的 demo 能用于文档中吗? 可以。在 demo 目录中象征性的从 story 文件中导入再默认导出 demo,然在在文档中通过 code 标签直接引用,demo 无需重复编写。 或者您也可以直接在文档中以 tsx/jsx 代码段从 story 文件中中引入 demo 并显示在文档中。
文档具体是怎么部署到 GitHub pages 的?
手动部署:使用 gh-page 可手动部署本地 build 目录到远端 gh-pages 分支。
使用 GitHub action 自动部署
在项目中新建.github/workflows 目录,添加 gp-ci.yml 如下:
yaml
name: CI
# Controls when the action will run.
on:
# Triggers the workflow on push or pull request events but only for the master branch
push:
branches: [ master ]
pull_request:
branches: [ master ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
publishDoc:
# The type of runner that the job will run on
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Install Packages # 安装依赖
run: npm install && npm install yargs-parser
- name: Cache node modules # 缓存依赖
uses: actions/cache@v1
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.OS }}-build-${{ env.cache-name }}-
${{ runner.OS }}-build-
${{ runner.OS }}-
- name: Build docs # 打包文档
run: npm run build:doc
- name: Publish docs # 发布文档
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
这时可以编写文档不用关注文档的打包与发布,直接合并到 master 分支即可,action 会自动打包和发布文档。
- 开发人员需要做什么? 只需要关注组件开发和文档编写,不需要关注部署与发布。
- yarn gen Component
- yarn story
- 编写组件和 demo
- 填写文档
- pull request
toolkit (工具库)
Github: toolkit Docs: xy-element website Getting start: Getting Started
现状
现在已经完成的工作:
- 工具打包与部署
- 文档管理
- 文档发布
- 单元测试
- 部分工具开发
TODO
- 工具整理与开发
- 完善文档
架构
- 文档: dumi
- 打包:rollup
- 开发语言:typescript
- 测试:jest
目录结构
txt
toolkit
├── LICENSE
├── README.en-US.md
├── README.md
├── _test_ // 测试目录
├── config
├── docs // 生成静态文档
├── guide // 文档原件
├── lib
├── package.json
├── public
├── rollup.config.js
├── src
├── tsconfig.json
└── yarn.lock
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
计划开发的工具类
工具类正在开发,没人每周开发并提交一个工具类,由团队内进行代码审核。
- broswer: 浏览器相关,如类型检测、版本检测
- fullscreen (event): 全屏模块
- store:localStorage 缓存
- time:时间格式化
- axios:axios 封装,包括请求拦截器
- httpclient:网络请求工具封装
- 节流 / 防抖
- websocket:websocket 相关方法封装
- logger:日志模块
- timeout/interval/requestanimation:定时器相关方法
- emmit:事件管理模块
- db:indexedDB 封装
- tranlateMoney (fixed):货币转换
- url parms:url 解析
- mergeDeep:对象深合并
- cloneDeep:深拷贝
- array/object/map: 常见数据结构方法封装
- native (native-js)(android,ios):native 模块封装