blog icon indicating copy to clipboard operation
blog copied to clipboard

渣渣灰手摸手带你开发一个组件库

Open clock157 opened this issue 7 years ago • 11 comments

大家好, 我是渣渣灰~ 本文的目标是带你开发一个组件库,并走通开发、测试、文档、打包还有发布流程。

准备环境

如果你想快速开始,也可以直接使用我们的脚手架

涉及工具: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.jsFoo为我们的第一个组件

.
├── .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,你可以使用 markdownjsx 语法来组织文档。

---
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>

再看下我们的开发环境,可以看到组件效果 屏幕快照 2019-02-06 23.26.51

组件测试

为了保证组件质量,我们需要引入组件测试,测试方案可以直接使用 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.jsonscripts 添加测试命令

  "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/

结语

示例完整代码

至此, 发布一个组件库的流程::搭建、开发、测试、打包、验证、发布、文档整个流程就走通了,在实际的开发过程中,你可能会遇到更多的问题,或者你对这篇教程有不理解的地方,都可以反馈我们。

钉钉群

这是手摸手系列第一篇, 本系列旨在为前端开发者服务, 探索前端开发的最佳实践. 后面会陆续出更多的教程, 欢迎留言提出你宝贵的建议和想了解的点.

clock157 avatar Feb 08 '19 10:02 clock157

yarn create umi --library 这样初始化更快吧。

yutingzhao1991 avatar Feb 11 '19 02:02 yutingzhao1991

yarn create umi --library 这样初始化更快吧。

用脚手架是要快一些,但那只是一个工具。这篇帖子偏向是教程,是想抛出一些点:让用户了解开发一个组件库的全貌,大概知道每一步完成的职责,应该注意什么。这是一个开头,稍后会对一些技术点,比如打包优化再进行深入。

clock157 avatar Feb 11 '19 03:02 clock157

update: 2019-03-07, 使用umi-library重构,更加小巧更加优雅。

clock157 avatar Mar 07 '19 14:03 clock157

您好,请问下,我在组件中使用了装饰器,并且运行 yarn run doc:dev 命令,但是还是提示我不支持装饰器, image

image

谢谢😀

jyzwf avatar Mar 08 '19 03:03 jyzwf

@jyzwf 稍后会有 pr 将一些常用 babel 插件集成。

clock157 avatar Mar 08 '19 03:03 clock157

@jyzwf 稍后会有 pr 将一些常用 babel 插件集成。

好的,,谢谢了🤓

jyzwf avatar Mar 08 '19 03:03 jyzwf

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启动项目也报这个错

haozhuanye avatar Mar 13 '19 09:03 haozhuanye

你安装依赖是怎么安装的? 使用 yarn install https://github.com/umijs/umi/issues/2072

clock157 avatar Mar 14 '19 08:03 clock157

您好,在组件打包那里遇到了问题: 执行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 avatar Jun 11 '19 09:06 fohui

@fohui 你好,umi-library 现在已升级为 https://github.com/umijs/father

clock157 avatar Jun 19 '19 08:06 clock157

@clock157 你好,请问怎么在antdesignpro的工程项目里,指定将某些文件打包为一个组件? 也就是说有一个功能点很好,然后希望把这个功能点的相关代码打包成一个包含js,css文件的插件,其他项目直接引用就可以看到该功能点。

ammagician avatar Aug 15 '19 07:08 ammagician