搭建一个公司私有React UI组件库

749 阅读3分钟

如何搭建一个公司私有React UI组件库

初始化工程项目

本项目以 vite 作为构建基础工具

pnpm create vite

选择 React + TS 模板 删除 index.html/tsconfig.app.json/tsconfig.node.json 文件 删除 src/App.tsx src/App.css src/index.css 文件

创建 src/components/ 目录 创建 src/components/index.tsx src/components/index.d.ts 文件 创建 src/components/Button.tsx src/components/index.tsx src/components/type.ts 文件

项目配置

  • tsconfig.json 配置 指定ts编译配置
{
  "compilerOptions": {
    "outDir": "dist",
    "module": "ESNext",
    "target": "ES5",
    "declaration": true,
    "jsx": "react",
    "moduleResolution": "Node",
    "allowSyntheticDefaultImports": true
  },
  "include": ["src/components"],
  "exclude": [
    "node_modules",
    "src/**/*.test.ts",
    "src/**/*.test.tsx",
    "src/**/*.stories.ts",
    "src/**/*.stories.tsx"
  ]
}
  • vite.config.ts 配置 需要修改 rollup 配置,新增以下 build 项配置内容
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],

  build: {
    lib: {
      entry: ["./src/components/index.ts"]
    },
    rollupOptions: {
      external: ["react", "react-dom", "antd"],
      experimentalLogSideEffects: false,
      treeshake: true,
      output: [
      {
        format: "cjs",
        dir: "dist/cjs",
        exports: "named",
        entryFileNames: "[name].js",
        chunkFileNames: "[name].js",
        assetFileNames: "[name].[ext]",
      },
      {
        format: "es",
        dir: "dist/es/",
        exports: "named",
        entryFileNames: "[name].js",
        chunkFileNames: "[name].js",
        assetFileNames: "[name].[ext]",
      }
    ]
    } 
  },
});

  • package.json 配置 指定包入口文件和声明文件
"main": "dist/index.js",
"module": "dist/es/index.js",
"types": "dist/index.d.ts",
"files": [
  "dist"
]

代码编写

  1. src/components/Button.tsx 组件内容
import React from "react";
import { Button as AntButton } from "antd";
import { ButtonProps } from "./type";

const Button = (props: ButtonProps) => {
  const localProps = {
    ...{
      primary: false,
      size: "middle",
      backgroundColor: "blue",
      label: "Button",
      onClick: () => {},
    },
    ...props,
  } satisfies ButtonProps;
  return (
    <AntButton {...localProps}>
      {localProps.children ?? localProps.label}
    </AntButton>
  );
};

export default Button;

  1. src/components/Button/type.ts
    用于定义组件的属性类型,也可直接写在组件内部,但为了更好的代码复用性,建议定义在单独的文件中。
export interface ButtonProps {
    /**
     * Is this the principal call to action on the page?
     */
    primary?: boolean;
    /**
     * What background color to use
     */
    backgroundColor?: string;
    /**
     * How large should the button be?
     */
    size?: "small" | "middle" | "large";
    /**
     * Button contents
     */
    label: string;
    /**
     * Optional click handler
     */
    onClick?: () => void;
    /**
     * Button contents
     */
    children?: React.ReactNode;
  }
  1. src/components/Button/index.tsx 组件出口文件,导出组件
import Button from './button';
export default Button;
  1. src/components/index.ts 所有组件出口文件,导出所有组件
export { default as XlButton } from "./Button";
// export { default as XlAlert } from "./Alert";

打包构建

基于vite.config.ts配置,和通常项目构建一致 执行

npm run build

打包输出资源目录dist,其结构如下 dist目录结构

dist
|-- Button
    |-- button.d.ts
    |-- button.js
    |-- index.d.ts
    |-- index.js
|-- OtherComponent
    |-- OtherComponent.d.ts
    |-- OtherComponent.js
    |-- index.d.ts
    |-- index.js
|-- index.d.ts
|-- index.js

测试

组件库需要充分测试后才打包构建发布至库管理平台 使用 npm link 命令可以实现包在本地的测试 在组件项目根目录执行 npm link 命令,将组件链接到本机全局 在测试项目根目录执行 npm link 组件名称 命令,将组件链接到本地,然后引入需要测试的组件,进行调试

发布

构建好的包需要发布之包管理平台

发布至npm

npm publish

发布至私有仓库

  1. 创建一个私有仓库 以 verdaccio 为例,根据 verdaccio 官网配置一个私有仓库,启动
  2. 配置npm的registry 在组件库的package.json中添加
"publishConfig": {
  // 私有仓库地址
  "registry": "http://localhost:4873"
}
  1. 添加npm用户
npm adduser --registry http://localhost:4873
  1. 发布
npm publish

注:包发布需要将private设置为false, package.json中 files 字段指定需要发布的文件。

文档生成

常用文档生成工具有 docz、storybook、dumi 等等 本项目采用 storybook 生成文档 初始化 storybook

npx storybook@latest init

以上命令会生成一个 .storybook 配置,然后执行 npm run storybook 命令即可启动 storybook 服务。

文档编写 如:在 src/components/Button/button.stories.ts 中添加如下代码

import type { Meta, StoryObj } from '@storybook/react';
import { fn } from '@storybook/test';
import  Button  from './button';

// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
const meta = {
  title: 'Example/Button',
  component: Button,
  parameters: {
    // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
    layout: 'centered',
  },
  // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
  tags: ['autodocs'],
  // More on argTypes: https://storybook.js.org/docs/api/argtypes
  argTypes: {
    backgroundColor: { control: 'color' },
  },
  // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
  args: { onClick: fn() },
} satisfies Meta<typeof Button>;

export default meta;
type Story = StoryObj<typeof meta>;

// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
export const Primary: Story = {
  args: {
    primary: true,
    label: 'Button',
  },
};

export const Secondary: Story = {
  args: {
    label: 'Button',
  },
};

export const Large: Story = {
  args: {
    size: 'large',
    label: 'Button',
  },
};

export const Small: Story = {
  args: {
    size: 'small',
    label: 'Button',
  },
};