如何创建和发布 React 组件库

2025-05-24

如何创建和发布 React 组件库

本教程中的所有代码作为完整包均可在此存储库中找到。

如果您对本教程的视频版本感兴趣,请查看下面的链接!您可以参考本博客中的代码进行操作。

(视频完全是可选的,博客文章中涵盖了每个步骤和说明)

  1. 介绍
  2. 先决条件和设置
  3. 创建组件
  4. 添加 Typescript
  5. 添加 Rollup
  6. 建立你的图书馆
  7. 发布你的图书馆
  8. 使用你的图书馆
  9. 添加 CSS
  10. 优化
  11. 添加测试
  12. 添加故事书
  13. 添加 SCSS
  14. 总结

介绍

本教程将指导您完成创建和发布您自己的自定义 React 组件库并将其托管在 Github 上的过程。

在本教程结束时,您将能够在所有未来的 React 项目中执行以下操作:



npm install @my-github-account/my-cool-component-library


Enter fullscreen mode Exit fullscreen mode


import MyCustomComponent from '@my-github-account/my-cool-component-library';

const MyApp = () => {
  return (
    <div>
      <MyCustomComponent />
    </div>
  )
}


Enter fullscreen mode Exit fullscreen mode

先决条件和设置

该项目假设您熟悉并已安装:

  • 代码编辑器/IDE(本教程使用 VS Code,但任何 IDE 都可以)
  • NPM(在计算机上安装 Node.js 时会安装 NPM)
  • 安装包(假设您知道如何使用将包添加到 Javascript 项目npm install
  • Bash 终端(或其他您习惯用来运行命令的终端)
  • Git(我们将在我们的机器上创建一个 git 存储库并将其发布到 Github,但所有说明都将提供有关如何继续的说明)
  • React(如何使用 JSX 创建简单组件)
  • Typescript(如何创建具有简单属性的对象接口)

首先我们将初始化我们的项目。



npm init


Enter fullscreen mode Exit fullscreen mode

您可以采用所有值的默认值,我们将在本教程的后面部分对其进行编辑。

接下来我们将添加创建组件所需的工具。



npm install react typescript @types/react --save-dev


Enter fullscreen mode Exit fullscreen mode

创建组件

现在我们可以创建第一个组件了。因为我们正在创建一个库,所以我们将为每一层创建索引文件,并从每一层导出组件,以便使用我们库的用户尽可能轻松地导入它们。

在项目的根目录中,创建以下文件结构:



.
├── src
│   ├── components
|   │   ├── Button
|   |   │   ├── Button.tsx
|   |   │   └── index.ts
|   │   └── index.ts
│   └── index.ts
├── package.json
└── package-lock.json


Enter fullscreen mode Exit fullscreen mode

请务必仔细检查你的结构。你应该包含三个index.ts文件,以及一个Button.tsx位于目录中的文件Button。如果你对在项目中构建 React 组件有自己偏好的方式,当然可以按照你喜欢的方式进行,但本教程将遵循以下结构。

首先创建Button.tsx

src/components/Button/Button.tsx



import React from "react";

export interface ButtonProps {
  label: string;
}

const Button = (props: ButtonProps) => {
  return <button>{props.label}</button>;
};

export default Button;


Enter fullscreen mode Exit fullscreen mode

为了简单起见,我们只需导出一个按钮,该按钮仅接受一个名为 的 prop label。确认基本模板设置正确后,我们可以为组件添加更多复杂性和样式。

在我们的按钮之后,我们更新按钮目录中的索引文件:

src/components/Button/index.ts



export { default } from "./Button";


Enter fullscreen mode Exit fullscreen mode

然后我们从组件目录中导出该按钮:

src/components/index.ts



export { default as Button } from "./Button";


Enter fullscreen mode Exit fullscreen mode

最后,我们将从基础src目录导出所有组件:

src/index.ts



export * from './components';


Enter fullscreen mode Exit fullscreen mode

添加 Typescript

到目前为止,我们还没有在项目中初始化 Typescript。虽然从技术上来说,使用 Typescript 不需要配置文件,但考虑到构建库的复杂性,我们肯定需要一个配置文件。

您可以通过运行以下命令来初始化默认配置:



npx tsc --init


Enter fullscreen mode Exit fullscreen mode

这将tsconfig.json在我们项目的根目录中创建一个文件,其中包含 Typescript 的所有默认配置选项。

如果您想了解更多关于文件中的众多选项,TS 的现代版本会自动为每个值创建描述性注释。此外,您还可以在此处tsconfig.json找到有关配置的完整文档

您可能会注意到,根据您的 IDE,初始化后项目会立即出现错误。这有两个原因:首先,Typescript 默认配置不支持 React;其次,我们还没有定义处理模块的方法:所以它可能无法理解如何管理我们所有的导出。

为了解决这个问题,我们将添加以下值tsconfig.json



{
  "compilerOptions": {
    // Default
    "target": "es5", 
    "esModuleInterop": true, 
    "forceConsistentCasingInFileNames": true,
    "strict": true, 
    "skipLibCheck": true,

    // Added
    "jsx": "react", 
    "module": "ESNext",  
    "declaration": true,
    "declarationDir": "types",
    "sourceMap": true,
    "outDir": "dist",
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "emitDeclarationOnly": true,
  }
}


Enter fullscreen mode Exit fullscreen mode

tsconfig.json我根据使用本文撰写时最新版本的 Typescript(4.4)创建的默认值,将这些值分成了几个不同的部分。注释掉的default值应该已经默认设置好了(不过,你需要仔细检查以确保)。

标记为“已添加”的是我们项目所需的新值。我们将简要概述我们需要它们的原因:

  • "jsx": "react" -- 将 JSX 转换为 React 代码
  • “module”:“ESNext”——为我们的库生成现代 JS 模块
  • “declaration”:true——.d.ts为我们的库类型输出一个文件
  • .d.ts"declarationDir": "types" --文件存放位置
  • "sourceMap": true -- 将 JS 代码映射回其 TS 文件来源以进行调试
  • “outDir”:“dist”——项目将生成的目录
  • "moduleResolution": "node" -- 遵循 node.js 规则查找模块
  • "allowSyntheticDefaultImports": true -- 如果没有手动创建导出,则假定使用默认导出
  • "emitDeclarationOnly": true -- 不生成 JS(rollup 会这么做)仅导出类型声明

将这些值添加到 TS 配置文件后,您会看到错误,Button.tsx并且其他文件会立即消失。

添加 Rollup

接下来,我们将Rollup添加到项目中。如果您以前从未使用过 Rollup,它与Webpack非常相似,都是将各个 JavaScript 模块捆绑到浏览器更易于理解的单一源码中的工具。

虽然两种工具都可以根据配置实现相同的目标,但通常情况下,webpack 用于打包应用程序,而 rollup 则更适合打包库(比如我们的库)。这就是我们选择 rollup 的原因。

与 webpack 类似,rollup 也使用插件生态系统。rollup 的设计使其无法做到面面俱到,它依赖于单独安装的插件来添加所需的功能。

我们将依靠四个插件来进行我们库的初始配置(稍后将添加更多插件):

重要更新!

随着时间的推移,其中一些工具已经更新,下面写的确切说明不再有效。评论区里有很多朋友慷慨地抽出时间进行了更正,我强烈建议您阅读这条评论,以便了解您可能遇到的错误和变化。

话虽如此,让我们继续安装汇总和我们的插件:



npm install rollup @rollup/plugin-node-resolve @rollup/plugin-typescript @rollup/plugin-commonjs rollup-plugin-dts --save-dev


Enter fullscreen mode Exit fullscreen mode

要配置汇总如何捆绑我们的库,我们需要在项目根目录中创建一个配置文件:

rollup.config.js



import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import typescript from "@rollup/plugin-typescript";
import dts from "rollup-plugin-dts";

const packageJson = require("./package.json");

export default [
  {
    input: "src/index.ts",
    output: [
      {
        file: packageJson.main,
        format: "cjs",
        sourcemap: true,
      },
      {
        file: packageJson.module,
        format: "esm",
        sourcemap: true,
      },
    ],
    plugins: [
      resolve(),
      commonjs(),
      typescript({ tsconfig: "./tsconfig.json" }),
    ],
  },
  {
    input: "dist/esm/types/index.d.ts",
    output: [{ file: "dist/index.d.ts", format: "esm" }],
    plugins: [dts()],
  },
];


Enter fullscreen mode Exit fullscreen mode

在这个文件中,我们导入了之前安装的四个插件。我们还将package.json文件作为 commonJS 模块的 int oa 变量导入,该变量名为packageJson。我们用这个变量来引用下一节中将要定义的mainmodule值。

我们库的入口点(输入)是目录index.tssrc导出所有组件的文件。我们将同时发布 ES6 和 CommonJS 模块,以便库的使用者可以选择最适合自己的类型。我们还会在导出数组中两个配置对象的第一个上调用四个插件中的三个。第一个配置定义了库的实际 JavaScript 代码是如何生成的。

第二个配置对象定义了我们的库类型如何分布并使用dts插件来实现。

运行第一个汇总之前的最后一步是在我们的package.json文件中定义“main”和“module”的值:

package.json



{
  "name": "template-react-component-library",
  "version": "0.0.1",
  "description": "A simple template for a custom React component library",
  "scripts": {
    "rollup": "rollup -c"
  },
  "author": "Alex Eagleson",
  "license": "ISC",
  "devDependencies": {
    "@rollup/plugin-commonjs": "^21.0.1",
    "@rollup/plugin-node-resolve": "^13.0.6",
    "@rollup/plugin-typescript": "^8.3.0",
    "@types/react": "^17.0.34",
    "react": "^17.0.2",
    "rollup": "^2.60.0",
    "rollup-plugin-dts": "^4.0.1",
    "typescript": "^4.4.4"
  },
  "main": "dist/cjs/index.js",
  "module": "dist/esm/index.js",
  "files": [
    "dist"
  ],
  "types": "dist/index.d.ts"
}


Enter fullscreen mode Exit fullscreen mode

这是我们在本教程中使用的文件示例package.json。当然,您的作者姓名可能不同,并且每个库的具体版本也可能不同。

最重要的变化如下:

  • “main”——我们定义了 commonjs 模块的输出路径
  • “module”——我们定义了es6模块的输出路径
  • “文件”——我们已经为整个库定义了输出目录
  • “类型”——我们已经定义了库类型的位置
  • “scripts”——我们定义了一个名为rollup的新脚本。它将使用 -c 标志运行 rollup 包,-c 标志表示“使用 rollup 配置文件”。如果您不熟悉文件中的脚本package.json,这些只是一些简写命令,您可以使用 来按名称运行npm run {SCRIPTNAME}。因此,运行这个脚本的命令是npm run rollup

建立你的图书馆

完成这些配置后,你就可以首次运行 rollup 了,并确保基本配置正确。运行前,你的项目结构应该如下所示:



.
├── src
│   ├── components
|   │   ├── Button
|   |   │   ├── Button.tsx
|   |   │   └── index.ts
|   │   └── index.ts
│   └── index.ts
├── package.json
├── package-lock.json
├── tsconfig.json
└── rollup.config.js


Enter fullscreen mode Exit fullscreen mode

每个文件的内容应如上所述。确认后,运行以下命令:



npm run rollup


Enter fullscreen mode Exit fullscreen mode

如果所有配置都正确,rollup 将正常运行,并且您将看到dist在项目根目录中创建一个目录,其结构如下:

dist 目录

(如果您收到错误,请务必仔细阅读并尝试找出问题所在。仔细检查每个文件是否完全遵循示例的结构。根据本教程发布以来的时间长短,库的新主要版本可能会发布,其中包含重大更改。package.json如果您需要指定特定版本,所有库版本的编号都显示在上面的示例中。)

发布你的图书馆

现在我们已经创建了组件库,我们需要一种方法来允许我们自己(或其他人)下载并安装它。我们将通过 NPM 托管在 Github 上来发布我们的库。首先,我们需要为我们的库创建一个仓库。

在 Github 上创建一个新的仓库。我已将其命名为template-react-component-library。然后按照步骤将您的项目初始化为 git 项目,并推送到您的新仓库。

登录 Github 并创建一个新的仓库,名字随意。在本例中,我已命名template-react-component-library,任何人都可以克隆并公开使用。如果您愿意,也可以选择将库设置为私有,本教程中描述的方法也适用于私有包(例如,如果您正在为公司创建库)。

创建存储库后,我们需要在本地项目中初始化 git。运行以下命令:



git init


Enter fullscreen mode Exit fullscreen mode

接下来在目录的根目录中创建一个.gitignore文件(特别注意前导句点,表示这是一个隐藏文件):

.gitignore



dist
node_modules


Enter fullscreen mode Exit fullscreen mode

在我们的文件中,.gitignore我们添加了distnode_modules目录。原因是这两个目录都是我们使用命令创建的自动生成的目录,因此无需将它们包含在我们的存储库中。

现在按照新存储库中显示的 Github 上的说明提交您的代码。

当您需要更改和更新组件库时,需要克隆和编辑您创建的这个仓库。它本身并非您(作为用户)安装和使用的包。为了在项目中配置包的发布位置,接下来我们需要package.json使用该信息进行更新:

package.json



{
  "name": "@YOUR_GITHUB_USERNAME/YOUR_REPOSITORY_NAME",
  "publishConfig": {
    "registry": "https://npm.pkg.github.com/YOUR_GITHUB_USERNAME"
  },
  ...  
}


Enter fullscreen mode Exit fullscreen mode

您将更新字段“name”的值,并添加一个名为“publishConfig”的新字段。请注意,以上大写字母的值应替换为您自己的值。例如,我的“name”字段值为@alexeagleson/template-react-component-library。请注意,“packageConfig”中也包含您的 Github 帐户名,但该值不以 @ 符号开头。

现在我们已经配置好了项目,接下来需要配置本地安装的NPM,以便获得发布到 Github 帐户的授权。为此,我们使用一个.npmrc文件。

此文件不属于我们的项目。这是一个位于中心位置的全局文件。对于 Mac/Linux 用户,它位于您的主目录中~/.npmrc

对于 Windows 用户,它也位于你的主目录中,尽管语法会有所不同。类似于C:\Users\{YOUR_WINDOWS_USERNAME}

有关此配置文件的更多信息,请阅读此文

创建文件后,对其进行编辑以包含以下信息:

~/.npmrc



registry=https://registry.npmjs.org/
@YOUR_GITHUB_USERNAME:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=YOUR_AUTH_TOKEN


Enter fullscreen mode Exit fullscreen mode

上面的示例中有两个大写字母的值需要替换。第一个是 YOUR_GITHUB_USERNAME。请确保包含前导 @ 符号。

第二个是 YOUR_AUTH_TOKEN,我们还没有创建。返回 Github!

前往你的 Github 个人资料:设置 -> 开发者设置 -> 个人访问令牌。或者点击此链接

点击生成新令牌。为其指定一个与您正在构建的项目相符的名称。并为其指定一个到期日期(出于安全考虑,Github 建议您不要创建无限期的令牌,但这取决于您)。

最重要的是点击write:packages访问值。这将授予你的令牌权限,允许你读取和写入 Github 帐户中的包,这正是我们所需要的。

生成新令牌

完成后,您可以点击创建令牌。Github 只会显示一次令牌。当您关闭/刷新页面时,令牌将消失,因此请确保将其复制到安全的位置(如果您使用密码管理器,可以复制到密码管理器中)。

您需要放置此令牌的主要位置是~/.npmrc您创建的文件中,用于替换YOUR_AUTH_TOKEN上述示例中的值。

在继续之前,请再进行一次完整性检查,确保您没有.npmrc在实际库项目的根目录中创建该文件。从技术上讲,这可以考虑,但您需要小心,因为您可能会意外地将其与库的其余代码一起提交到您的 Github 仓库,并将您的令牌暴露给公众。如果您的.npmrc文件位于您的主目录中,则可以将这种风险降至最低。

此时,一旦您的~/.npmrc文件添加了您的 Github 用户名和访问令牌,请返回您的项目目录并运行以下命令:



npm publish


Enter fullscreen mode Exit fullscreen mode

(如果提示您输入登录凭据,则用户名是您的 Github 用户名,密码是您生成的访问令牌)

恭喜!您现在已经发布了 React 组件库 0.0.1 版本!您可以在 Github 帐户中查看它,方法是:前往您的主帐户仪表板,点击“repositories”右上方的“packages”:

Github 软件包

使用你的图书馆

现在您的图书馆已经上线,您一定会想使用它!

请注意,如果您将库发布到私有仓库,使用方法会略有不同。所有尝试导入该库的用户(除了您自己的机器)如果未获得授权,都会收到404 Not Found错误。

这些用户还需要添加一个~/.npmrc包含相同信息的文件。为了更安全,您可以为这些用户提供一个仅具有读取权限(无写入权限)的访问令牌。

(从现在开始,我们将假定您已完成该步骤,或者正在使用公共存储库。)

由于我们已经使用 React 和 Typescript 创建了一个组件库,因此我们假设库的使用者也会使用这些工具。从技术上讲,我们所有的类型文件(.d.ts)都是补充性的:这意味着如果使用标准 JavaScript,它们会被忽略,因此使用我们的库并非必须使用 Typescript。如果需要,类型文件就在那里。

在我们的示例中,我们将使用它来确认它们是否正常工作。我们将使用最流行且最简单的方法之一来初始化一个 React 应用:Create React App

在新目录中运行以下命令

(请记住,我们正在模拟其他用户下载和安装我们的库,因此该项目应该与库本身完全分开)



npx create-react-app my-app --template typescript


Enter fullscreen mode Exit fullscreen mode

打开新my-app创建的目录并运行:



npm run start


Enter fullscreen mode Exit fullscreen mode

确认您能够打开并加载默认应用程序屏幕localhost:3000(或它打开的任何端口)。

现在来测试一下我们的库。在新my-app项目的根目录下,运行以下命令:



npm install @YOUR_GITHUB_USERNAME/YOUR_REPOSITORY_NAME


Enter fullscreen mode Exit fullscreen mode

以我的项目为例:npm install @alexeagleson/template-react-component-library

假设您的令牌和配置设置正确,一切都将正确安装(如果有任何问题,请重新查看~/.npmrc配置示例。)

现在my-app在您选择的 IDE(例如 VS Code)中打开项目并导航到该src/App.tsx文件。

当您添加<Button />组件时,如果您的编辑器支持导入自动完成(ctrl/cmd + .对于 VS Code),那么您将看到它借助 Typescript 自动识别我们的库导出该按钮。

自动导入

让我们添加它!最简单的更新示例src/App.tsx是:

src/App.tsx



import React from "react";
import { Button } from "@alexeagleson/template-react-component-library";

function App() {
  return <Button label="Hello world!"/>;
}

export default App;


Enter fullscreen mode Exit fullscreen mode

当我们npm run start再次运行时,角落里就会出现我们的Hello world!按钮。

你好世界按钮

就这样!恭喜!现在你拥有了使用 Typescript 创建和发布 React 组件库所需的所有工具!本教程到此结束,如果你愿意,可以继续学习。

如果您选择继续,我们将研究如何扩展我们的组件库以包含许多非常有用的功能,例如:

  • CSS:用于导出具有样式的组件
  • Storybook:用于在设计组件时在库内测试组件
  • React 测试库和 Jest:用于测试我们的组件

添加 CSS

在进行任何其他配置之前,我们首先要创建一个 CSS 文件,用于为按钮应用一些样式。在Button组件所在的目录中,我们将创建一个名为的文件Button.css

src/components/Button/Button.css



button {
  font-size: 60px;
}


Enter fullscreen mode Exit fullscreen mode

这会将我们常规的Hello world!按钮变成一个非常大的按钮。

接下来,我们将指出这些样式将应用于我们的按钮组件。我们将使用非 JavaScript 原生的特殊语法,但借助 Rollup 和相应的插件,我们可以使用它。Button.tsx使用以下内容更新我们的文件:

src/components/Button/Button.tsx



import React from "react";
import "./Button.css";

export interface ButtonProps {
  label: string;
}

const Button = (props: ButtonProps) => {
  return <button>{props.label}</button>;
};

export default Button;


Enter fullscreen mode Exit fullscreen mode

请注意import './Button.css'已添加的。

现在我们需要告诉 rollup 如何处理该语法。为此,我们使用一个名为 的插件rollup-plugin-postcss。运行以下命令:



npm install rollup-plugin-postcss --save-dev


Enter fullscreen mode Exit fullscreen mode

接下来我们需要更新我们的汇总配置:

rollup.config.js



import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import typescript from "@rollup/plugin-typescript";
import dts from "rollup-plugin-dts";

// NEW
import postcss from "rollup-plugin-postcss";

const packageJson = require("./package.json");

export default [
  {
    input: "src/index.ts",
    output: [
      {
        file: packageJson.main,
        format: "cjs",
        sourcemap: true,
      },
      {
        file: packageJson.module,
        format: "esm",
        sourcemap: true,
      },
    ],
    plugins: [
      resolve(),
      commonjs(),
      typescript({ tsconfig: "./tsconfig.json" }),

      // NEW
      postcss(), 
    ],
  },
  {
    input: "dist/esm/types/index.d.ts",
    output: [{ file: "dist/index.d.ts", format: "esm" }],
    plugins: [dts()],

    // NEW
    external: [/\.css$/],
  },
];



Enter fullscreen mode Exit fullscreen mode

注意注释中标明的三行新代码NEW。在dts配置中,我们需要指定.css模块是外部的,并且不应作为类型定义的一部分进行处理(否则会报错)。

最后,我们需要更新文件中的版本号package.json。记住,我们发布的是一个包,所以当我们进行更改时,我们需要确保不会影响之前版本库的用户。每次发布时,都应该增加版本号:

package.json



{
  "version": "0.0.2",
  ...
}


Enter fullscreen mode Exit fullscreen mode

现在运行以下命令:



npm run rollup
npm publish


Enter fullscreen mode Exit fullscreen mode

在库使用端(my-app本教程中的 React 应用),我们也需要更新以获取包的最新版本。最简单的方法是在 文件中增加版本号package.jsonmy-app它应该显示^0.0.1。将其增加到^0.0.2,然后就可以使用以下npm install命令进行更新:



npm install
npm run start


Enter fullscreen mode Exit fullscreen mode

您将获得我们库中一个支持捆绑 CSS 的巨型按钮组件!

大按钮

优化

我们可以针对此设置进行一些简单的优化。首先是添加一个名为terser的插件,它可以最小化我们的包并减少整体文件大小。

更新:看到这个评论说 rollup 现在有自己更好的维护版本terser

另一种方法是将部分依赖项更新为peerDependencies。使用 rollup 的peer dependency插件,我们可以告诉使用我们库的项目需要哪些依赖项(例如 React),但实际上不会将 React 的副本与库本身捆绑在一起。如果使用者的项目中已经安装了 React,则会使用它,否则,运行时会安装 React npm install

首先我们来安装这两个插件:



npm install rollup-plugin-peer-deps-external rollup-plugin-terser --save-dev


Enter fullscreen mode Exit fullscreen mode

然后我们将更新我们的汇总配置:

rollup.config.js



import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import typescript from "@rollup/plugin-typescript";
import postcss from "rollup-plugin-postcss";
import dts from "rollup-plugin-dts";

//NEW
import { terser } from "rollup-plugin-terser";
import peerDepsExternal from 'rollup-plugin-peer-deps-external';

const packageJson = require("./package.json");

export default [
  {
    input: "src/index.ts",
    output: [
      {
        file: packageJson.main,
        format: "cjs",
        sourcemap: true,
      },
      {
        file: packageJson.module,
        format: "esm",
        sourcemap: true,
      },
    ],
    plugins: [
      // NEW
      peerDepsExternal(),

      resolve(),
      commonjs(),
      typescript({ tsconfig: "./tsconfig.json" }),
      postcss(),

      // NEW
      terser(),
    ],
  },
  {
    input: "dist/esm/types/index.d.ts",
    output: [{ file: "dist/index.d.ts", format: "esm" }],
    plugins: [dts()],
    external: [/\.css$/],
  },
];


Enter fullscreen mode Exit fullscreen mode

然后我们将文件中的React 从 移动devDependenciespeerDependenciespackage.json

package.json



{
  "devDependencies": {
    "@rollup/plugin-commonjs": "^21.0.1",
    "@rollup/plugin-node-resolve": "^13.0.6",
    "@rollup/plugin-typescript": "^8.3.0",
    "@types/react": "^17.0.34",
    "rollup": "^2.60.0",
    "rollup-plugin-dts": "^4.0.1",
    "rollup-plugin-peer-deps-external": "^2.2.4",
    "rollup-plugin-postcss": "^4.0.1",
    "rollup-plugin-terser": "^7.0.2",
    "typescript": "^4.4.4"
  },
  "peerDependencies": {
    "react": "^17.0.2"
  },
  ...


Enter fullscreen mode Exit fullscreen mode

添加测试

为了给我们的组件添加测试,我们将安装React Testing Library,为了运行这些测试,我们将安装Jest



npm install @testing-library/react jest @types/jest --save-dev


Enter fullscreen mode Exit fullscreen mode

在我们的 Button 目录中,创建一个名为Button.test.tsx

src/components/Button/Button.test.tsx



import React from "react";
import { render } from "@testing-library/react";

import Button from "./Button";

describe("Button", () => {
  test("renders the Button component", () => {
    render(<Button label="Hello world!" />);
  });
});


Enter fullscreen mode Exit fullscreen mode

这段代码的作用是将按钮渲染到非浏览器 DOM 实现上,并确保其正确挂载。这是一个非常简单的测试,但它可以作为入门语法的一个很好的示例。想要更深入地了解,请参阅 React 测试库文档

在运行测试之前,我们需要配置 jest,并在我们的 中创建一个测试运行器脚本package.json。我们将从配置开始,jest.config.js在项目根目录中创建一个文件:

jest.config.js



module.exports = {
  testEnvironment: "jsdom",
};


Enter fullscreen mode Exit fullscreen mode

这告诉 Jest 使用jsdom作为我们的 DOM 实现。

更新:jsdom现在必须将其作为最新版本的手动依赖项添加,请参阅此评论以获取更多信息

接下来更新您的package.json文件:

package.json



{
  "scripts": {
    "rollup": "rollup -c",
    "test": "jest"
  },
  ...
}


Enter fullscreen mode Exit fullscreen mode

现在我们可以用以下方法运行测试:



npm run test


Enter fullscreen mode Exit fullscreen mode

不幸的是,我们会报错!这个错误发生在遇到我们的 JSX 代码时。如果你还记得的话,我们使用 Typescript 通过 rollup 配置来处理 JSX,并使用了一个 Typescript 插件来教 rollup 如何处理 JSX。遗憾的是,Jest 中没有这样的设置。

我们需要安装Babel来处理 JSX 转换。我们还需要安装一个名为 的 Jest 插件,babel-jest它会告诉 Jest 使用 Babel!现在就安装它们,以及用于处理 Typescript 和 React 代码的 Babel 插件。所有这些插件的安装包如下所示:



npm install @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript babel-jest --save-dev


Enter fullscreen mode Exit fullscreen mode

现在我们在项目根目录中创建 Babel 配置文件,它告诉 Babel 使用我们刚刚安装的所有这些插件:

babel.config.js



module.exports = {
  presets: [
    "@babel/preset-env",
    "@babel/preset-react",
    "@babel/preset-typescript",
  ],
};


Enter fullscreen mode Exit fullscreen mode

现在我们应该能够运行我们的测试npm run test...但是...还有一个问题!

你会收到一条错误信息,提示无法识别import.css文件的。这很正常,因为我们postcss为 rollup 配置了一个插件来处理这个问题,但 Jest 却没有这样做。

最后一步是安装一个名为identity-obj-proxy的包。它的作用是允许你配置Jest,将任何类型的导入都视为通用对象。因此,我们将对CSS文件执行此操作,以免出现错误。



npm install identity-obj-proxy --save-dev 


Enter fullscreen mode Exit fullscreen mode

我们需要更新 Jest 配置以包含该moduleNameMapper属性。我们还添加了less和 ,scss以便您以后扩展项目时使用这些属性:

jest.config.js



module.exports = {
  testEnvironment: "jsdom",
  moduleNameMapper: {
    ".(css|less|scss)$": "identity-obj-proxy",
  },
};


Enter fullscreen mode Exit fullscreen mode

现在,如果您已经按照步骤操作到了这一步,那么您可以运行:



npm run test


Enter fullscreen mode Exit fullscreen mode

您将获得一次成功的测试!

Jest 测试

添加故事书

Storybook 是一款用于在网站/应用程序之外可视化 UI 组件的工具。它非常适合用于原型设计和测试组件的不同视觉状态,以确保它们按照设计的方式运行,而不会因屏幕上显示其他不相关的组件而产生额外开销。

它还为您提供了一种在库项目中处理组件时轻松查看和使用组件的方法,而无需构建不必要的测试页面来显示它们。

初始化 Storybook 非常简单。要设置并自动配置它,我们只需运行以下命令:



npx sb init --builder webpack5


Enter fullscreen mode Exit fullscreen mode

(请注意,截至撰写本文时,Storybook 仍然默认使用 webpack 4,这就是我们添加额外构建器标志的原因。大概 5 很快就会成为默认设置,因此将来可能就没有必要了)

另请参阅此评论devDependency,其中解释了当您仅使用 Storybook 来测试组件库时如何正确设置它。

与我们迄今为止添加的其他一些工具不同,Storybook 更像是一个“内置电池”的软件包,可以为您处理大部分初始设置。它甚至会自动scripts将运行所需的功能添加到您的package.json文件中。

你还会注意到,它会stories在你的目录中创建一个目录src。这个目录中充满了预先构建的模板,你可以将其用作如何创建自己的故事的示例。我建议你在熟悉 Storybook 以及如何编写自己的故事之前不要删除这些模板,将它们放在身边会非常方便。

现在让我们为按钮创建一个简单的故事。在Button目录中创建一个名为的新文件Button.stories.tsx

src/components/Button/Button.stories.tsx



import React from "react";
import { ComponentStory, ComponentMeta } from "@storybook/react";
import Button from "./Button";

// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
export default {
  title: "ReactComponentLibrary/Button",
  component: Button,
} as ComponentMeta<typeof Button>;

// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
const Template: ComponentStory<typeof Button> = (args) => <Button {...args} />;

export const HelloWorld = Template.bind({});
// More on args: https://storybook.js.org/docs/react/writing-stories/args
HelloWorld.args = {
  label: "Hello world!",
};

export const ClickMe = Template.bind({});
ClickMe.args = {
  label: "Click me!",
};


Enter fullscreen mode Exit fullscreen mode

一开始这可能有点让人不知所措,但当你一点一点地看完它时,你会发现它相当简单。

  • 默认导出定义了按钮在 Storybook 中的显示位置。我选择了ReactComponentLibrary作为简单名称,以便将自定义组件与示例组件分开分组。

  • 模板确定实际渲染哪个组件,以及对其应用哪些默认参数/属性

  • Template.bind对象是组件的实例或示例状态。因此,在实际项目中,你可能会看到类似“LargeButton”和“SmallButton”这样的对象。由于我们的按钮始终是大按钮因此我刚才使用了两个不同标签来测试按钮的示例。

如果你查看package.json文件,你会发现 Storybook 已经添加了一个storybookstorybook-build脚本。第一个脚本用于在本地托管 Storybook 应用程序,以便快速轻松地进行测试。第二个脚本将构建一个静态 HTML/JS 包,可以轻松地托管在远程服务器上,以便团队的所有成员都可以试用你的组件。

现在我们只需运行:



npm run storybook


Enter fullscreen mode Exit fullscreen mode

补充:您可能会因为缺少依赖项而遇到错误。如果发生这种情况,有几种解决方案。

第一种是手动安装这些依赖项。例如react-dom。这并不理想,因为你的项目本身不应该依赖于这些库,所以没有必要包含它们,因为它们包含在 Storybook 的对等依赖项中,例如这里

如果您直接运行一个新npm install命令,它将安装peerDependencies您正在使用的所有库。运行此命令之前,您可能需要删除package-lock.jsonnode_modules目录。全新安装后,它们将自动重新生成。

解决库之间依赖项重叠和缺失的问题可能比较棘手。请耐心等待,并确保阅读你的错误信息!


如果一切顺利,您将看到一个友好的界面,可以实时浏览示例组件以及您自己的自定义按钮。单击它们即可查看您创建的不同状态。

故事书示例

关于 Storybook 还有很多内容需要了解,请务必阅读文档

添加 SCSS

感谢rollup-plugin-postcss你,你应该已经能够简单地将.css文件重命名为.scss,然后import 'Button.scss就可以继续了。运行程序num run rollup将使用当前配置顺利编译所有内容。

然而,要让它与 Storybook 一起运行则是另一回事。请注意,这也是我们--builder webpack5在上一节安装时使用该标志的主要原因,您在尝试使用 webpack 4 配置 Storybook 支持 SCSS 时可能会遇到很多错误。在版本 5 中,使用 SCSS 预设会相当简单。

(如果您遵循了本教程的早期版本,则可能已使用默认的 webpack 4 初始化 Storybook。您可以从package.json文件中删除与 Storybook 相关的任何内容。接下来删除您的package-lock.json/node_modules/目录,然后使用标志再次初始化 Storybook --builder webpack5



npm install @storybook/preset-scss css-loader sass sass-loader style-loader --save-dev


Enter fullscreen mode Exit fullscreen mode

要了解有关不同类型的 CSS 支持和 Storybook 的更多信息,请单击此处

(如果你想进一步了解这些不同的加载器之间的区别,这里有一个关于Stack Overflow的很棒的答案)

然后您需要做的就是添加@storybook/preset-scss到您的主 Storybook 配置中:

.storybook/main.js



module.exports = {
  "stories": [
    "../src/**/*.stories.mdx",
    "../src/**/*.stories.@(js|jsx|ts|tsx)"
  ],
  "addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@storybook/preset-scss"
  ],
  "core": {
    "builder": "webpack5"
  }
}


Enter fullscreen mode Exit fullscreen mode

现在您将能够运行npm run storybook并查看所有 SCSS 样式。

(最后提醒一下,Storybook 经常会遇到依赖项错误。在开始安装它所要求的缺少的依赖项之前,请务必先尝试删除package-lock.jsonnode_modules然后npm install再次运行。这通常可以解决您的问题,而无需您在自己的项目中添加不必要的依赖项。)

总结

现在你应该对如何创建自己的 React 组件库有了很好的理解。这样做不仅可以让你了解 JavaScript 包管理生态系统的工作原理,还能让你通过简单的命令轻松访问跨项目使用的代码。

请查看我的其他学习教程。如果您觉得其中有任何内容有用,请随时发表评论或提出问题并与他人分享:


想要了解更多类似教程,请在 Twitter 上关注我@eagleson_alex

文章来源:https://dev.to/alexeagleson/how-to-create-and-publish-a-react-component-library-2oe
PREV
如何使用 Node.js 备份您的个人文件(并在此过程中学习一些 webdev 技能)
NEXT
如何在 React 中创建暗黑模式组件