从零到一搭建React组件库

最近一直在捣鼓如何搭建React组件库,至于为何会产生这个想法,主要是由于组件库对于前端生态来讲究极重要,每个着眼于长远发展、看重开发效率的的互联网公司基本上都会量身定制本身的组件库,它的好处不用多说。对于前端工程师而言,去理解以及掌握它,可让咱们在从此的工做中以及应聘过程当中多出一项特殊技能,而且对自身的纵向发展也就是颇有利的。下面是我记录我在搭建组件库的过程。css

初始化工程

搭建工程不打算采用create-react-app脚手架来搭建,由于脚手架封装好了不少东西,而有些东西对于组件库并不适用,用来搭建组件库过于臃肿,所以我不打算借助任何脚手架来搭建工程。前端

首先,先建立一个工程文件夹pony-react-ui,在该文件夹下执行以下命令:react

npm init // 生成package.json
tsc --init // 生成tsconfig.json

而后,按照以下目录结构初始化工程:webpack

pony-react-ui
├── src
    ├── assets
    ├── components
        ├── Button
            ├── Button.tsx
            └── index.ts
        └── Dialog
            ├── Dialog.tsx
            └── index.ts
    ├── styles
        ├── _button.scss
        ├── _dialog.scss
        ├── _mixins.scss
        ├── _variables.scss
        └── pony.scss
    └── index.ts // 打包的入口文件,引入pony.scss,抛出每个组件
├── index.js // 入口文件,package.json中main字段指定的文件
├── package.json
├── tsconfig.json
├── webpack.config.js
└── README.md

编写一个Button组件

Button组件应该知足一下需求:web

  • 不一样尺寸
  • 不一样类型
  • 不一样颜色
  • 禁用状态
  • 点击事件

Button.tsxnpm

import React from 'react';
import classNames from 'classnames';

export interface IButtonProps {
  onClick?: React.MouseEventHandler;
  // 类型
  primary?: boolean;
  secondary?: boolean;
  outline?: boolean;
  dashed?: boolean;
  link?: boolean;
  text?: boolean;
  // 尺寸
  xLarge?: boolean;
  large?: boolean;
  small?: boolean;
  xSmall?: boolean;
  xxSmall?: boolean;
  // 颜色
  success?: boolean;
  warn?: boolean;
  danger?: boolean;
  // 禁用状态
  disabled?: boolean;
  className?: string;
  style?: React.CSSProperties;
  children?: React.ReactNode;
}

export const Button = (props: IButtonProps) => {
  const {
    className: tempClassName,
    style,
    onClick,
    children,
    primary,
    secondary,
    outline,
    dashed,
    link,
    text,
    xLarge,
    large,
    small,
    xSmall,
    xxSmall,
    success,
    danger,
    warn,
    disabled,
  } = props;
  
  
  const className = classNames(
    {
      'pony-button': true,
      'pony-button-primary': primary,
      'pony-button-secondary': secondary && !text,
      'pony-button-outline': outline,
      'pony-button-dashed': dashed,
      'pony-button-link': link,
      'pony-button-text': text && !secondary,
      'pony-button-text-secondary': secondary && text,
      'pony-button-round': round,
      'pony-button-rectangle': noRadius,
      'pony-button-fat': fat,
      'pony-button-xl': xLarge,
      'pony-button-lg': large,
      'pony-button-sm': small,
      'pony-button-xs': xSmall,
      'pony-button-xxs': xxSmall,
      'pony-button-long': long,
      'pony-button-short': short,
      'pony-button-success': success,
      'pony-button-warn': warn,
      'pony-button-danger': danger,
      'pony-button-disabled': disabled,
    },
    tempClassName
  );
  
  return (
    <button 
      type="button"
      className={className}
      style={style}
      onClick={onClick}
      disabled={disabled}>
      <span className="pony-button__content">{children}</span>
    </button>
  );
}

在Button/index.ts文件中抛出Button组件以及定义的类型json

export * from './Button';

这样,一个示例组件就基本完成了,有同窗确定会有这么一个疑问,为何在Button.tsx中没有引入它的样式文件_button.scss,而是在使用时引入全局样式或者单独引入_button.scss呢?前端工程师

// 单独引入组件样式
import { Button } from 'pony-react-ui';
import 'pony-react-ui/lib/styles/button.scss';

// 全局引入组件样式,打包时抽离出来的样式
import 'pony-react-ui/lib/styles/index.scss';

由于这跟样式的权重有关,经过import引入的样式权重将低于JSX中className定义的样式,所以才能够在组件外部修改内部的样式。app

举个实例:ui

import { Button } from 'pony-react-ui';
import 'pony-react-ui/lib/styles/button.scss';
import styles from './index.module.scss';

const Demo = () => (
  <div className={styles.btnBox}>
    <Button onClick={submit}>submit</Button>
  </div>
)

引入组件库中的Button.scss和本地的index.module.scss在打包后会以<style></style>注入到页面中,并且顺序确定是:

<style type="text/css">
  // Button.scss的样式
</style>

<style type="text/css">
  // index.module.scss的样式
</style>

所以,index.module.scss中的样式权重是高于Button.scss中的样式,能够在index.module.scss中修改Button.scss的样式

编写样式

打包输出UMD规范

打包输出es module规范

docz生成组件使用文档

发布到npm仓库

相关文章
相关标签/搜索