渣渣灰手摸手带你开发一个组件库
大家好, 我是渣渣灰~ 本文的目标是带你开发一个组件库,并走通开发、测试、文档、打包还有发布流程。
准备环境
如果你想快速开始,也可以直接使用我们的脚手架
涉及工具:umi-library
初始化项目
# 创建目录
$ mkdir umi-library-demo && cd umi-library-demo
# 初始化
$ yarn init -y
# 安装依赖
$ yarn add umi-library --save-dev
添加配置文件 .umirc.library.js
export default {
entry: 'src/index.js',
esm: 'rollup',
cjs: 'rollup'
}
给 package.json 添加 script:
+ "scripts": {
+ "doc:dev": "umi-lib doc dev"
+ },
这时, 你已经可以通过以下命令跑起来:
$ yarn run doc:dev
浏览器访问 http://127.0.0.1:8001/,即可看到我们的组件开发环境。
开发组件
规划目录结构, 入口为 src/index.js,Foo为我们的第一个组件
.
├── .umirc.library.js # 配置
├── package.json
└── src
├── Foo # 组件
│ └── index.js
└── index.js # 入口
Foo 组件代码如下:
// src/Foo/index.js
import * as React from 'react';
export default function(props) {
return (
<button
style={{
fontSize: props.size === 'large' ? 40 : 20,
}}
>
{ props.children }
</button>
);
}
接下来跑一下我们的组件,在 src/Foo 目录下创建 index.mdx,基于 mdx,你可以使用 markdown 加 jsx 语法来组织文档。
---
name: Foo
route: /
---
import { Playground } from 'docz';
import Foo from './';
# Foo Component
## Normal Foo
<Foo>Hi</Foo>
## Large Foo with playground
<Playground>
<Foo size="large">Hi</Foo>
</Playground>
再看下我们的开发环境,可以看到组件效果

组件测试
为了保证组件质量,我们需要引入组件测试,测试方案可以直接使用 umi-test
$ yarn add umi-test --save-dev
在 src/Foo 目录新建测试文件 index.test.js
import { shallow } from 'enzyme';
import Foo from './index.js';
describe('<Foo />', () => {
it('render Foo', () => {
const wrapper = shallow(<Foo size="large">hello, umi</Foo>);
expect(wrapper.prop('style').fontSize).toEqual(40);
expect(wrapper.children().text()).toEqual('hello, umi');
});
});
然后在 package.json 的 scripts 添加测试命令
"scripts": {
"doc:dev": "umi-lib doc dev",
+ "test": "umi-test"
},
执行测试命令
$ yarn run test
执行结果,测试通过!
PASS src/Foo/index.test.js
<Foo />
✓ render Foo (39ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 11.701s
Ran all test suites.
✨ Done in 15.82s.
组件打包
组件开发测试完成后,需要打包成不同的产物以适应不同的场景。默认使用 rollup 打包生成三个格式的包:
-
cjs: CommonJs,能被 Node 和 打包工具如 webpack 使用。 -
esm: ES Module,支持静态分析可以 tree shaking。 -
umd: Universal Module Definition 通用包,既能像cjs一样被使用,也可以发布到 cdn,通过 script 的方式被浏览器使用。
修改 package.json
- "main": "index.js",
+ "main": "dist/index.js",
+ "module": "dist/index.esm.js",
"scripts": {
"doc:dev": "umi-lib doc dev",
+ "dev": "umi-lib build --watch",
+ "build": "umi-lib build",
"test": "umi-test"
},
使用命令
# 监控文件变化并打包
$ yarn run dev
# 打包
$ yarn run build
打包结果
$ yarn run build
yarn run v1.13.0
$ umi-lib build
ℹ info Clean dist directory
ℹ info Build cjs with rollup
ℹ info Build esm with rollup
✨ Done in 2.75s.
验证产物
为了验证我们的产物是否可用,我们可以基于 umi 创建一个小 demo 使用一下,,在项目下创建目录 example,目录结构:
example/
└── pages
└── demo-foo
└── index.js
我们创建了 demo-foo 这个页面, 并使用 Foo 组件,其 index.js 代码:
import { Foo } from '../../../dist';
export default function() {
return (
<Foo size="large">hello, world</Foo>
);
}
我们跑一下
$ cd example
$ umi dev
# 如果没有 umi 这个命令, 请安装
$ yarn global add umi
启动好以后,console 会提示访问地址,打开后访问页面 /demo-foo,就可以看到效果:

发布组件
组件开发好,发布到 npm registry 就可以被大家使用,也可以发布到私有 registry 内部使用。如果没有 npm 账号需要先注册,然后登陆 yarn login。
修改 package.json,添加发布 script,发布前执行测试用例,并且包里只含 dist 目录:
+ "files": ["dist"],
"scripts": {
+ "pub": "yarn run test && yarn publish",
"test": "umi-test"
},
执行命令
$ yarn run pub
发布成功后,你就可以在 npm 看到 umi-library-demo
对于其他用户,就可以使用以下命令来安装使用这个包。
# 使用 yarn
$ yarn add umi-library-demo --save
#使用 npm
$ npm install umi-library-demo --save
发布文档
在我们的组件开发完毕,文档相应写完后我们需要打包和部署文档,以便使用者查阅。
首先修改 package.json,添加 script:
"scripts": {
"doc:dev": "umi-lib doc dev",
+ "doc:build": "umi-lib doc build",
+ "doc:deploy": "umi-lib doc deploy",
},
umi-library 默认将文档部署到 github.io, url 规则是 https://{yourname}.github.io/{your-repo},我们需要修改 .umirc.library.js 配置一下文档静态资源的前缀base。
export default {
entry: 'src/index.js',
esm: 'rollup',
cjs: 'rollup',
+ doc: {
+ base: '/umi-library-demo'
+ }
}
接下来执行命令:
# 打包文档
$ yarn run doc:build
# 部署文档, 速度取决于网速
$ yarn run doc:deploy
部署成功后,以这个项目为例, 文档地址为:
https://clock157.github.io/umi-library-demo/
结语
至此, 发布一个组件库的流程::搭建、开发、测试、打包、验证、发布、文档整个流程就走通了,在实际的开发过程中,你可能会遇到更多的问题,或者你对这篇教程有不理解的地方,都可以反馈我们。
钉钉群
这是手摸手系列第一篇, 本系列旨在为前端开发者服务, 探索前端开发的最佳实践. 后面会陆续出更多的教程, 欢迎留言提出你宝贵的建议和想了解的点.
用 yarn create umi --library 这样初始化更快吧。
用
yarn create umi --library这样初始化更快吧。
用脚手架是要快一些,但那只是一个工具。这篇帖子偏向是教程,是想抛出一些点:让用户了解开发一个组件库的全貌,大概知道每一步完成的职责,应该注意什么。这是一个开头,稍后会对一些技术点,比如打包优化再进行深入。
update: 2019-03-07, 使用umi-library重构,更加小巧更加优雅。
您好,请问下,我在组件中使用了装饰器,并且运行 yarn run doc:dev 命令,但是还是提示我不支持装饰器,


谢谢😀
@jyzwf 稍后会有 pr 将一些常用 babel 插件集成。
@jyzwf 稍后会有 pr 将一些常用 babel 插件集成。
好的,,谢谢了🤓
Module parse failed: Unexpected token (473:30) You may need an appropriate loader to handle this file type. | async function loadFromImports(path) { | // tslint:disable-next-line
const { imports } = await import('~imports');
| const { default: Component$$1, getInitialData } = await importspath; | const ExportedComponent = props => (createElement(AsyncComponent, Object.assign({}, props, { as: Component$$1 || 'div', getInitialData: getInitialData })));
我在本地npm run doc:dev启动的时候,报这个错,大神能帮忙看一下么,我直接用yarn create umi --library启动项目也报这个错
你安装依赖是怎么安装的? 使用 yarn install https://github.com/umijs/umi/issues/2072
您好,在组件打包那里遇到了问题: 执行yarn run build之后提示是这样的:
$ umi-lib build
ℹ info Clean dist directory
ℹ info Build cjs with rollup
'createElement' is not exported by 'node_modules/react/index.js'
ℹ info Build esm with rollup
'createElement' is not exported by 'node_modules/react/index.js'
✨ Done in 3.54s.
rollup打包的结果也是有问题:没有获取到React.createElement方法
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
if (process.env.NODE_ENV === 'production') {
module.exports = require('./cjs/react.production.min.js');
} else {
module.exports = require('./cjs/react.development.js');
}
function index (props) {
return undefined("button", {
style: {
fontSize: props.size === 'large' ? 40 : 20
}
}, props.children);
}
exports.Foo = index;
然后我改了.umirc.library.js配置为:
esm: 'babel',
cjs: 'babel'
打包结果是正确、可执行的,但是多出了es和lib两个目录。
我的问题是: 1.rollup的配置有问题是什么情况,改怎么解决呀? 2.怎么做配置,把babel的打包结果输出到指定文件,file属性失效了? 3.umi-library项目404了
@fohui 你好,umi-library 现在已升级为 https://github.com/umijs/father
@clock157 你好,请问怎么在antdesignpro的工程项目里,指定将某些文件打包为一个组件? 也就是说有一个功能点很好,然后希望把这个功能点的相关代码打包成一个包含js,css文件的插件,其他项目直接引用就可以看到该功能点。