这篇文章会介绍一个 React 组件库项目的搭建、打包、发布

但不会涉及组件库文档站点的构建,如有需要,建议查看《使用 dumi 打包 React 组件库并生成文档站点》

另外,虽然本文介绍的是 React 组件库,但对于 Vue 组件库也是通用的

 

 

一、创建项目

首先参考 Vite 的文档创建一个项目

yarn create vite my-packages --template react-ts

// 这里的 my-packages 是项目名称,按需修改

生成的项目如下:

vite打包报错 heap out of memory vite打包组件库_typescript

结构很简单,但对于一个组件库来说,还需要完善

首先是 package.json, 需要将 dependencies 中的基础库移到 peerDependencies 和 devDependencies 中:

"dependencies": {
  "classnames": "^2.3.1"
},
"peerDependencies": {
  "react": "^18.2.0",
  "react-dom": "^18.2.0"
},
"devDependencies": {
  "react": "^18.2.0",
  "react-dom": "^18.2.0"
}

// react 版本根据实际需求做调整

这样就不会把 react 打包到组件库里面

然后我们还可以引入 ESLint、Husky 等工具来统一规范

除此之外,我认为有必要创建两个目录:

一个演示页面目录 example,用于在开发过程中查看效果

一个管理源码的目录 packages,用于打包发布

 

 

二、组件开发

初始化的项目中有一个 src 目录,可以先重命名为 example,用作开发过程中的调试页面

Vite 项目开发环境的入口文件是根目录下的 index.html,其默认引入的是 /src/main.tsx  文件

现在由于演示页面的目录已经改为 example,所以这里也需要调整:

<script type="module" src="/example/main.tsx"></script>

 

然后新建一个 packages

对于大部分的组件,都会有这三部分:组件 Component、组件创建的 TS 类型 types、组件样式 styles

以一个简单的 Button 组件为例:

vite打包报错 heap out of memory vite打包组件库_ci_02

在开发的时候,可以直接在 Button.tsx 完成 Component 和 types

然后在 index.tsx 中统一导出

// packages/Button/index.tsx
import Button from './Button';
export type { ButtonProps } from './Button';
export default Button;
import './styles/index.less';

// 这里同时引入了 styles

然后在 packages/index.ts中引入并统一导出:

// packages/index.ts
export { default as Button } from './Button';
export type { ButtonProps } from './Button';

这样就能在使用组件库的时候,从组件库的入口文件同时拿到 Component 和 types:

import { Button } from 'my-packages';
import type { ButtonProps } from 'my-packages';

上面是在组件中直接引入了相应的 styles,也可以在最外层统一引入

但最终打包的时候(默认配置)都会整合到一个 style.css 中

 

 

三、组件打包

Vite 提供了一个库模式用于打包组件库,直接在 vite.config.ts 中配置即可

// vite.config.ts
import path from 'path';
import { defineConfig } from 'vite';

function resolve(str: string) {
  return path.resolve(__dirname, str);
}

export default defineConfig({
  build: {
    // 打包输出的目录
    outDir: 'lib',
    // 防止 vite 将 rgba() 颜色转化为 #RGBA 十六进制
    cssTarget: 'chrome61',
    lib: {
      // 组件库源码的入口文件
      entry: resolve('packages/index.ts'),
      // 组件库名称
      name: 'MyPackages',
      // 文件名称, 打包结果举例: my-packages.umd.cjs
      fileName: 'my-packages',
    },
    rollupOptions: {
      // 确保外部化处理那些你不想打包进库的依赖
      external: ['react', 'react-dom'],
      output: {
        // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
        globals: {
          react: 'react',
          'react-dom': 'react-dom',
        },
      },
    },
  }
})

// 如果引入 'path' 的时候报错,检查一下是否安装了 @types/node

以上 build 配置算是一个最小配置,可以参考官网的《构建选项》查看完整配置


vite 的生产构建使用的是 rollup,以目前的配置只会构建出 js 代码,对于 typescript 类型,需要借助 rollup 的 typescript 插件来实现

yarn add @rollup/plugin-typescript -D

然后在 vite.config.ts 中使用插件

import { defineConfig } from 'vite';
import path from 'path';
import react from '@vitejs/plugin-react';
import typescript from '@rollup/plugin-typescript';

function resolve(str: string) {
  return path.resolve(__dirname, str);
}

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    react(),
    typescript({
      target: 'es5',
      rootDir: resolve('packages/'),
      declaration: true,
      declarationDir: resolve('lib'),
      exclude: resolve('node_modules/**'),
      allowSyntheticDefaultImports: true,
    }),
  ],
  build: {
    // ...
  },
});

打包配置到这里就完成了,执行 yarn build 命令就能打包组件库

vite打包报错 heap out of memory vite打包组件库_typescript_03

 

  

四、发布到 npm

作为一个组件库,在发布到 npm 之前需要调整一下 package.json

{
  "name": "my-packages",
  "version": "0.1.0",
  "type": "module",
  "files": [
    "lib"
  ],
  "main": "./lib/my-packages.umd.cjs",
  "module": "./lib/my-packages.js",
  "typings": "./lib/index.d.ts",
  "exports": {
    ".": {
      "import": "./lib/my-packages.js",
      "require": "./lib/my-packages.umd.cjs"
    }
  }
}

上面设置了 files 字段,其作用和 .npmignore 文件类似

只是 files 是规定哪些文件/目录会放在包内上传到 npm,而 .npmignore 是排除不需要上传的文件

如果需要上传到私有库,可以在 package.json 中定义:

{
  "publishConfig": {
    "registry": "https://your.npm.private.com"
  }
}

然后登录 npm 账户,直接发布即可

npm login

npm version patch

npm publish