從零到一搭建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倉庫

相關文章
相關標籤/搜索