教你使用Dumi和father-build快速搭建React组件库

1. 背景

目前我所在业务组是负责后台管理系统,可是对于使用的组件并无引入第三方库,全有开发人员本身编写。可是对于Common Project仍是处于拷贝的方式。而后萌生了对公共组件库进行管理的想法。这个方式有什么好处呢?
javascript

  • 可以沉淀组内组件,避免重复造轮子的缺陷。
  • 统一维护组件,使不一样开发人员遵循同一套使用标准。
  • 本身的UI库,可以保证系统视觉和交互一致性。
  • 提升产品研发效率,资源重复利用,减小重复开发。
  • 项目统一设计、编码语言规则,沟通更轻松。
  • 专一业务,在视觉要求方面减小花费时间,争取更好的时间写业务。

2. 技术选择(dumi + father-build)

固然,在网上进行查询,发现不少不少人都作过相似的事情,咱们能够借鉴不少的经验。有人是本身编写的配置。而我在芸芸博客中,筛选了知足个人条件的技术(dumi + father-build)。为什么选择它呢?
css

  • dumi 是蚂蚁金服插件化的企业级前端框架,专门为组件开发场景而生的文档工具。与father的文档打包相比,构建编译速度更快,更友好。
  • father-build属于father(集文档与组件打包一体的库)的一部分。专一于组件打包。

为何我不选择本身编写呢?不只仅是搭建时间的成本,还有后续优化等,目前我没有自信会比专一于该业务的人作的更好。后续也会去好好学习一下大神是如何实现的,可是迫在眉睫的是我但愿完成个人组件库搭建,所以先偷个小懒了^^。html

3. 文档库搭建

@umijs/create-dumi-lib是基于Umi的文档工具,开箱即用,专一于开发和文档编写。运行如下命令,会初始化项目结构。前端

$ npm init
$ npx @umijs/create-dumi-lib
复制代码

文档目录结构


.umirc.ts

是umi项目的配置文件,而dumi是umi在组件库开发中的一个最佳实践,可是它本质仍是一个umi插件,所以只要umi的配置,都是能够在dumi中正常使用的。java

.fatherrc.ts

是father-build的配置文件,组件库如何编译以及编译后产生的类型都须要在这里使用。node

启动

运行npm start便可启动。
react

4. 编写组件以及引用文档

编写一个简单的实例和docs。git

4.1 配置官网logo(.umirc.ts)

tips: 资源须要放在public目录下面。例如public/image/xxx.png。否则会访问不了。es6

// .umirc.ts

import { defineConfig } from 'dumi';
let BaseUrl = ''
export default defineConfig({
    mode: 'site', // site: 站点模式(导航头 + 左侧菜单 + 右侧内容)。 doc:文档
    title: 'Biz Library', // 组件库名称
    favicon: BaseUrl + '/images/favicon.ico', 
    logo: BaseUrl + '/images/photos.svg',
    description: '用于Biz Web Dev 前端组件开发。',
});
复制代码

4.2 编写组件与组件文档

component:/src/Dialog/index.tsx

import React, { FC } from 'react';
import './index.less';
interface DialogProps {
  onClick?: React.MouseEventHandler<HTMLElement>;
  type?: 'default' | 'primary' | 'secondary';
  disabled?: boolean;
}
const Dialog: FC<DialogProps> = ({ children, onClick, type = 'dialog' }) => {
  return (
    <div className={'biz_dev_dialog'} onClick={onClick}> {children} </div>
  );
};
export default Dialog;
复制代码

css:/src/Dialog/index.less

@btn-prefix-cls: ~'biz_dev';

.@{btn-prefix-cls} {
  &_dialog {
    padding: 6px 16px;
    font-size: 12px;
    min-width: 64px;
    color: red;
  }
}
复制代码

文档编写

  • 组件文档en: docs/component/dialog.md
  • 组件文档中文: docs/component/dialog.zh-CN.md
  • 包名biz-web-library: 是package.json 中的name
import React from 'react';
import { Dialog } from 'biz-web-library'; // 可经过包名引入,而不是相对路径
export default () => <Dialog>dialog example</Dialog>;
复制代码

启动

运行npm run start,至此本地的开发环境就已经完成啦,你能够一边开发一边调试了。
github

5. CI 测试

测试对于组件库来讲,是必不可少的。在father-build 中,集成了jest用于单元测试。可是对于React组件进行测试还须要进行额外的配置。

jest.config.js

module.exports = {
  setupFiles: ['<rootDir>/testSetUp.js'],
  testEnvironment: 'jsdom', // default: jsdom, 可配置: node
};
复制代码

testSetUp.js

记得安装 enzyme-adapter-react-16enzyme

import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
复制代码

编写测试用例

import React from 'react';
import { render } from 'enzyme';
import Button from '../../src/Button';

describe('test', () => {
	it('test render', () => {
		let Wrapper = render(<Button>case one</Button>);
		expect(true).toBeTruthy();
	})
})
复制代码

运行npm run test,执行经过。

6. 全量加载 和 按需加载

咱们知道,不少组件库是将组件统一打包,经过如下方式引入。默认状况下father-build也是这样的。

import { componentName } from 'biz-library';
复制代码

常见的库(例如antd)是经过配置babel插件实现按需引入。而我但愿个人组件库能够不经过配置插件,便可实现按需引入。

import componentName from 'biz-library/componentName';
复制代码

6.1 实现按需加载

.fatherrc.ts

做为father-build的配置文件,用于配置组件库被如何编译和编译产物的类型。通常设置 esm: 'rollup'就够用了。但它缺点是会打包在一块儿,没法实现按需引入。所以我采用esm: 'babel'

export default {
  target: 'browser',
  esm: 'babel',
  lessInBabelMode: true, // babel 模式下作 less 编译
  entry: ['src/Button/index.tsx', 'src/Dialog/index.tsx'],
  autoprefixer: {
    browsers: ['ie>9', 'Safari >= 6'],
  },
  pkgs: [
    // 组件依赖构建顺序, 例如 a组件依赖于b组件,那么须要先编译 b,在编译a,则 这里能够控制组件编译顺序
  ],
};
复制代码

tsconfig.json

其余属性没什么好说的, "declaration": true是必须配置的,生成typescript项目中.d.ts后缀的文件。

{
  "compileOnSave": true,
  "compilerOptions": {
    "target": "es6",
    "module": "esnext",
    "lib": ["es2018", "dom"],
    "rootDir": "./",
    "outDir": "es",
    "moduleResolution": "node",
    "importHelpers": true,
    "jsx": "react",
    "esModuleInterop": true,
    "sourceMap": true,
    "baseUrl": "./",
    "strict": true,
    "declaration": true, // 只有declaration设置为true才能生成.d.ts后缀的文件
    "allowJs": true,
    "forceConsistentCasingInFileNames": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noImplicitAny": true,
    "strictNullChecks": false,
    "suppressImplicitAnyIndexErrors": true,
    "noUnusedLocals": true,
    "experimentalDecorators": true,
    "skipLibCheck": true,
    "paths": {
      "@/*": ["src/*"],
      "@@/*": ["src/.umi/*"],
      "@@@/*": ["docs/*"]
    },
    "allowSyntheticDefaultImports": true,
    "typeRoots": ["typings", "node_modules/@types"]
  },
  "include": ["src/**/*", "dosc/**/*"],
  "exclude": [
    "node_modules",
    "lib",
    "es",
    "dist",
    "typings",
    "**/__test__",
    "test"
  ]
}
复制代码

打包发布至npm

运行npm run build命令,本地打包后,将组件一个个拆分了的,为咱们按需引入作好了铺垫。

运行npm run release,将其发布到npm上,即可以经过npm进行安装,在项目中进行使用。发布至npm需注意version: 1.0.0每一次不能相同。

import React from 'react';
import Button from 'biz-web-library/es/Button';
function App() {
  return (<Button>click me</Button>);
}
export default App;
复制代码

打包路径修改

从上面的使用方式能够看出,Button并无在biz-web-library的下面,我其实期待按需引入使用如下方式:

import Button from 'biz-web-library/Button';
复制代码

通过查询,终于找到了一个比较优雅的使用方式,Click Me
package.json中配置如下语句,而后直接运行npm publish便可。

"prepublishOnly": "npm run build && cp -r ./es/* . && rm -rf ./es",
"postpublish": "git clean -fd",
"release": "npm publish",
复制代码

7. 发布组件官网 到 GitHub Pages

至此,我但愿的组件库编写已经完成,既能够全量引入,也能够按需引入。那么如何让别人可以看到咱们的组件库呢。这里我使用github pages

github 配置

运行npm run deploy可将文档部署至github的github pages上。这至关于你组件的官网。请确保github组件仓库的设置以下:(Path: Settings => Options => GitHub Pages:)

咱们能够看到生成的地址https://yezizhao.github.io/biz-web-library/,它包含了厂库的名称,所以你访问改地址时,会发现jscss出现404不能访问了。此时不要慌,说明打包产生的路径不正确。在.umirc.ts文件中,将let BaseUrl = '/biz-web-library';修改便可。而后在从新运行npm run deploy进行发布便可。

import { defineConfig } from 'dumi';

let BaseUrl = '/biz-web-library'; // 仓库的路径

export default defineConfig({
  // 网站描述配置
  mode: 'site',
  title: 'Biz Library',
  favicon: BaseUrl + '/images/favicon.ico',
  logo: BaseUrl + '/images/photos.svg',
  description: '用于Biz Web Dev 前端组件开发。',

  // 打包路径配置
  base: BaseUrl,
  publicPath: BaseUrl + '/', // 打包文件时,引入地址生成 publicPath/xxx.js
  outputPath: 'docs-dist',
  exportStatic: {}, // 对每隔路由输出html
  dynamicImport: {}, // 动态导入

  hash: true, //加hash配置,清除缓存
  manifest: {
    // 内部发布系统规定必须配置
    fileName: 'manifest.json',
  },

  // 多国语顺序
  locales: [
    ['en-US', 'English'],
    ['zh-CN', '中文'],
  ],

  // 主题
  theme: {
    '@c-primary': '#16c35f',
  },
});

复制代码

dumi css丢失

如今,你的网页能够访问了,可是此时的css却不见了。实际上是由于 md中引入的css是指向构建后的目录,不能直接指向源码目录。我是在package.json中配置sideEffects解决了该问题。

"sideEffects": ["./src/**/*.less"],
复制代码

78. 结束语

至此,一个简易的组件库开发环境和发布环境就已经搭建完成,可是上面的例子实际上是用的公网的github。而公司内部组件并不该该发布到公网,所以你还须要搭建一个内部的私有npm。就不进行描述了。

github example实例: github.com/YeziZhao/bi…