教程中的所有代码作为一个完整的包都可以在此存储库中找到。
如果您对本教程的视频版本感兴趣,请查看下面的链接!您可以按照此博客中的代码进行操作。
(视频完全是可选的,博客文章中涵盖了每一步和说明)
介绍
本教程将带您完成创建和发布您自己的自定义 React 组件库并将其托管在 Github 上的过程。
在本教程结束时,您将能够在未来的所有 React 项目中执行以下操作:
npm install @my-github-account/my-cool-component-library
import MyCustomComponent from '@my-github-account/my-cool-component-library';
const MyApp = () => {
return (
<div>
<MyCustomComponent />
</div>
)
}
先决条件和设置
本项目假设您熟悉并已安装:
- 代码编辑器/IDE(本教程使用 VS Code,但任何 IDE 都可以使用)
- NPM(当你在你的机器上安装 Node.js 时安装了 NPM)
- 安装包(假设您知道如何使用 将包添加到 Javascript 项目
npm install
) - Bash 终端(或其他适合运行命令的终端)
- Git(我们将在我们的机器上创建一个 git 存储库并将其发布到 Github,尽管将提供有关如何遵循的所有说明)
- React(如何使用 JSX 创建简单的组件)
- Typescript(如何创建具有简单属性的对象接口)
首先,我们将初始化我们的项目。
npm init
您可以为所有值采用默认值,我们将在本教程的稍后部分对其进行编辑。
接下来,我们将添加创建组件所需的工具。
npm install react typescript @types/react --save-dev
创建组件
现在我们可以创建我们的第一个组件。因为我们正在创建一个库,我们将为每一层创建索引文件,并从每一层导出我们的组件,以使使用我们库的人尽可能轻松地导入它们。
在项目的根目录中,创建以下文件结构:
.
├── src
│ ├── components
| │ ├── Button
| | │ ├── Button.tsx
| | │ └── index.ts
| │ └── index.ts
│ └── index.ts
├── package.json
└── package-lock.json
确保仔细检查您的结构。你应该有三个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;
为了简单起见,我们将只导出一个按钮,该按钮带有一个名为label
. 一旦我们确认我们的基本模板设置正确,我们就可以为我们的组件添加更多的复杂性和样式。
在按钮之后,我们更新 Button 目录中的索引文件:
src/components/Button/index.ts
export { default } from "./Button";
然后我们从组件目录中导出该按钮:
src/components/index.ts
export { default as Button } from "./Button";
最后,我们将从基础src目录中导出所有组件:
src/index.ts
export * from './components';
添加打字稿
到目前为止,我们还没有在我们的项目中初始化 Typescript。尽管从技术上讲,使用 Typescript 不需要配置文件,但对于构建库的复杂性,我们肯定需要一个。
您可以通过运行以下命令来初始化默认配置:
npx tsc --init
这将tsconfig.json
在我们项目的根目录中为我们创建一个文件,其中包含 Typescript 的所有默认配置选项。
如果您想了解有关tsconfig.json
文件中许多选项的更多信息,现代版本的 TS 将自动为每个值创建描述性注释。此外,您可以在此处找到有关配置的完整文档。
您可能会注意到,根据您的 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,
}
}
在tsconfig.json
撰写本文 (4.4) 时,我根据使用最新版本的 Typescript 创建的默认值将这些值分为几个不同的部分。默认情况下,默认情况下应该已经为您设置了注释为default的值(您需要仔细检查并确保)。
标记为已添加的值是我们项目所需的新值。我们将简要概述我们为什么需要它们:
- “jsx”: “react” — 将 JSX 转换为 React 代码
- “module”: “ESNext” — 为我们的库生成现代 JS 模块
- “declaration”: true —
.d.ts
为我们的库类型输出一个文件 - “declarationDir”: “types” — 放置
.d.ts
文件的位置 - “sourceMap”: true — 将 JS 代码映射回其 TS 文件来源以进行调试
- “outDir”: “dist” — 将生成项目的目录
- “moduleResolution”: “node” — 按照 node.js 规则查找模块
- “allowSyntheticDefaultImports”: true — 如果没有手动创建,则假定默认导出
- “emitDeclarationOnly”: true — 不生成 JS(汇总会这样做)只导出类型声明
将这些值添加到 TS 配置文件后,您应该会看到错误Button.tsx
并且其他文件会立即消失。
添加汇总
接下来我们将汇总添加到我们的项目中。如果您以前从未使用过 rollup,那么它与webpack非常相似,因为它是一个工具,用于将单个 Javascript 模块捆绑到浏览器更能理解的单个源中。
尽管这两种工具都可以根据配置完成相同的目标,但通常 webpack 用于捆绑应用程序,而 rollup 特别适合捆绑库(如我们的)。这就是我们选择汇总的原因。
与 webpack 类似,rollup 使用插件生态系统。按照设计,汇总并不知道如何做所有事情,它依赖于单独安装的插件来添加您需要的功能。
我们将依赖四个插件来对我们的库进行初始配置(稍后会添加更多):
- @rollup/plugin-node-resolve -对模块使用节点解析算法
- @rollup/plugin- typescript – 教汇总如何处理 Typescript 文件
- @rollup/plugin-commonjs – 将 commonjs 模块转换为 ES6 模块
- rollup-plugin-dts – 汇总您的
.d.ts
文件
话虽如此,让我们继续安装 rollup 和我们的插件:
npm install rollup @rollup/plugin-node-resolve @rollup/plugin-typescript @rollup/plugin-commonjs rollup-plugin-dts --save-dev
要配置 rollup 如何捆绑我们的库,我们需要在项目的根目录中创建一个配置文件:
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()],
},
];
在这个文件中,我们导入了我们安装的四个插件。我们还将我们的package.json
文件作为 commonJS 模块 int oa 变量导入packageJson
。我们使用这个变量来引用我们将在下一节中定义的主值和模块值。
我们库的入口点(输入)是导出我们所有组件index.ts
的src
目录中的文件。我们将分发 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"
}
这是package.json
我们用于本教程的文件示例。显然,您的作者姓名可能不同,每个库的特定版本也可能不同。
最重要的变化如下:
- “main” — 我们已经定义了 commonjs 模块的输出路径
- “module” — 我们已经定义了es6模块的输出路径
- “files” — 我们已经为整个库定义了输出目录
- “types” — 我们已经定义了库类型的位置
- “脚本”——我们定义了一个名为rollup的新脚本。这将使用 -c 标志运行汇总包,这意味着“使用汇总配置文件”。如果你不熟悉脚本的
package.json
文件,这些都是简单的,你可以通过名字与运行速记命令npm run {SCRIPTNAME}
。所以运行这个将是npm run rollup
。
建立你的图书馆
有了这些配置,您现在可以第一次运行汇总并确保您的基本配置正确。在运行之前,您的项目结构应如下所示:
.
├── src
│ ├── components
| │ ├── Button
| | │ ├── Button.tsx
| | │ └── index.ts
| │ └── index.ts
│ └── index.ts
├── package.json
├── package-lock.json
├── tsconfig.json
└── rollup.config.js
每个文件的内容应如上所述。确认后,运行以下命令:
npm run rollup
如果一切都配置正确,rollup 将正常运行,您将看到dist
在项目根目录中创建的目录,其结构如下所示:
(如果您收到错误,请务必仔细阅读以尝试找出问题所在。仔细检查您的每个文件是否完全遵循示例的结构。根据自本教程发布以来经过的时间,新专业库的版本可能会随着重大更改而发布。package.json
如果您需要指定特定版本,则在示例中可以看到所有版本的库编号)
发布您的图书馆
现在我们已经创建了我们的组件库,我们需要一种允许我们自己(或其他人)下载和安装它的方法。我们将通过 NPM 通过托管在 Github 上发布我们的库。首先,我们需要为我们的库创建一个存储库。
在 Github 上创建一个新的存储库。我已经命名了我的template-react-component-library
。然后按照步骤将您的项目初始化为 git 项目,并推送到您的新存储库。
登录 Github 并创建一个新的存储库,随意命名。对于这个例子,我给template-react-component-library
它起了个标题,每个人都可以克隆和公开使用它。如果您愿意,您可以选择将您的库设为私有,本教程中描述的方法也适用于私有包(例如,如果您正在为您的公司创建库)。
创建存储库后,我们需要在本地初始化项目中的 git。运行以下命令:
git init
接下来.gitignore
在目录的根目录创建一个文件(特别注意前导句点,表示这是一个隐藏文件):
.gitignore
dist
node_modules
在我们的.gitignore
文件中,我们添加了dist
和node_modules
目录。原因是这两个目录都是我们使用命令创建的自动生成的目录,因此无需将它们包含在我们的存储库中。
现在按照新存储库中显示的 Github 上的说明提交代码。
您创建的这个存储库是您要对组件库进行更改和更新时克隆和编辑的存储库。这不是您(作为用户)将安装和使用的软件包本身。要在需要将包发布到的项目中进行配置,接下来我们需要package.json
使用该信息进行更新:
package.json
{
"name": "@YOUR_GITHUB_USERNAME/YOUR_REPOSITORY_NAME",
"publishConfig": {
"registry": "https://npm.pkg.github.com/YOUR_GITHUB_USERNAME"
},
...
}
您将更新字段“名称”值并添加一个名为“publishConfig”的新字段。请注意,上面大写的值旨在替换为您自己的值。例如,我的“姓名”字段值为@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
在上面的示例中,大写字母中有两个值要替换。第一个是 YOUR_GITHUB_USERNAME。确保包含前导@ 符号。
第二个是我们尚未创建的 YOUR_AUTH_TOKEN。回到 Github!
转到您的 Github 个人资料:设置 -> 开发人员设置 -> 个人访问令牌。或者直接点击这个链接
单击生成新令牌。给它一个适合您正在构建的项目的名称。给它一个到期日期(出于安全原因,Github 建议您不要创建具有无限寿命的令牌,但这取决于您)。
最重要的是点击write:packages
访问值。这将使您的令牌权限能够读取和写入您的 Github 帐户的包,这正是我们所需要的。
完成后,您可以单击以创建令牌。Github 只会向您展示代币一次。当您关闭/刷新页面时,它将消失,因此请确保将其复制到安全位置(如果您使用密码管理器,则可能是密码管理器)。
您需要放置此令牌的主要位置是在~/.npmrc
您创建的文件中替换上例中的YOUR_AUTH_TOKEN
值。
在继续之前,再做一次完整性检查以确保您没有.npmrc
在实际库项目的根目录中创建文件。这在技术上是一个选项,但是您需要小心的原因是您可能会不小心将它与库代码的其余部分一起提交到您的 Github 存储库,并将您的令牌公开给公众。如果您的.npmrc
文件在您的主目录中,则这种风险会降到最低。
此时,一旦您的~/.npmrc
文件添加了您的 Github 用户名和访问令牌,请返回您的项目目录并运行以下命令:
npm publish
(如果系统提示您输入登录凭据,则用户名是您的 Github 用户名,您的密码是您生成的访问令牌)
恭喜!您现在已经发布了 React 组件库的 0.0.1 版!您可以在您的 Github 帐户上查看它,方法是转到您的主帐户仪表板并单击“存储库”右侧顶部的“包”::
使用您的图书馆
现在您的库已上线,您会想要使用它!
请注意,如果您发布到私有存储库,则使用库的说明会略有不同。如果未经授权,尝试导入它的每个人(除了您自己的机器)都会收到404 Not Found错误。
这些用户还需要添加~/.npmrc
具有相同信息的文件。但是,为了更安全,您可以为这些用户提供只有读取权限而不是写入权限的访问令牌。
(从现在开始,我们将假定您已完成该步骤,或者正在使用公共存储库。)
由于我们使用 React 和 Typescript 创建了一个组件库,因此我们假设我们库的使用者也将使用这些工具。从技术上讲,我们所有的类型文件(.d.ts)
都是补充性的:这意味着如果使用标准 Javascript,它们将被简单地忽略,因此没有必要使用 Typescript 来使用我们的库。如果需要,类型就在那里。
但是,对于我们的示例,我们将使用它,以便我们可以确认它们是否正常工作。我们将使用最流行和最简单的方法之一初始化 React 应用程序:Create React App。
在新目录中运行以下命令:
(记住我们是在模拟其他用户下载和安装我们的库,所以这个项目应该与库本身完全分开)
npx create-react-app my-app --template typescript
打开my-app
创建的新目录并运行:
npm run start
确认您能够打开并加载默认应用程序屏幕localhost:3000
(或它打开的任何端口)。
现在是我们图书馆的测试。从新my-app
项目的根目录,运行以下命令:
npm install @YOUR_GITHUB_USERNAME/YOUR_REPOSITORY_NAME
因此,对于我的项目,例如: 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;
当我们npm run start
再次运行时,角落里藏着我们的Hello world!按钮。
就是这样!恭喜!您现在拥有使用 Typescript 创建和分发 React 组件库所需的所有工具!此时,您将结束本教程,并根据需要自行继续。
如果您选择继续,我们将研究如何扩展我们的组件库以包含许多非常有用的功能,例如:
- CSS:用于导出带有样式的组件
- 故事书:用于在我们设计组件时在库本身中测试我们的组件
- React 测试库和 Jest:用于测试我们的组件
添加 CSS
在进行任何其他配置之前,我们将首先创建一个 CSS 文件,该文件将一些样式应用于我们的 Button。在Button
组件所在的目录中,我们将创建一个名为Button.css
:
src/components/Button/Button.css
button {
font-size: 60px;
}
这将变成我们常规的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;
请注意import './Button.css'
已添加的 。
现在我们需要告诉 rollup 如何处理该语法。为此,我们使用了一个名为rollup-plugin-postcss
. 运行以下命令:
npm install rollup-plugin-postcss --save-dev
接下来我们需要更新我们的汇总配置:
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$/],
},
];
请注意注释中指示的三个新行NEW
。在dts
配置中,我们需要指定.css
模块是外部的,不应作为我们类型定义的一部分进行处理(否则我们会得到一个错误)。
最后,我们需要更新版本号在我们的package.json
文件。请记住,我们正在发布一个包,因此当我们进行更改时,我们需要确保不会影响我们库以前版本的用户。每次我们发布时,我们都应该增加版本号:
package.json
{
"version": "0.0.2",
...
}
现在运行这些命令:
npm run rollup
npm publish
在库使用端(my-app
我们教程中的 React 应用程序),我们还需要更新以获取最新版本的包。最简单的方法是递增的版本号package.json
的文件my-app
。它应该显示^0.0.1
. 将其增加到^0.0.2
,然后您可以使用以下npm install
命令进行更新:
npm install
npm run start
您将从我们的库中看到一个巨大的按钮组件,它现在支持捆绑 CSS!
优化
我们可以使用此设置进行一些简单的优化。第一个是添加一个名为terser的插件,它将缩小我们的包并减小整体文件大小。
另一种是将我们的一些依赖项更新为peerDependencies
. 使用 rollup 的对等依赖项插件,我们可以告诉使用我们的库的项目需要哪些依赖项(如 React),但实际上不会将 React 的副本与库本身捆绑在一起。如果消费者已经在他们的项目中安装了 React,它将使用它,否则将在他们运行时安装npm install
。
首先我们将安装这两个插件:
npm install rollup-plugin-peer-deps-external rollup-plugin-terser --save-dev
然后我们将更新我们的汇总配置:
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$/],
},
];
然后我们将 React 从 移动devDependencies
到peerDependencies
我们的package.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"
},
...
添加测试
要为我们的组件添加测试,我们将安装React 测试库,并且要运行这些测试,我们将安装Jest。
npm install @testing-library/react jest @types/jest --save-dev
在我们的 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!" />);
});
});
这将做的是在非浏览器 DOM 实现上呈现我们的按钮,并确保它正确安装。这是一个非常简单的测试,但它是您可以用来开始使用的语法的一个很好的示例。要更深入地阅读 React 测试库文档。
在我们可以运行测试之前,我们需要配置 jest,并在我们的package.json
. 我们将从配置开始jest.config.js
,在项目的根目录中创建一个文件:
jest.config.js
module.exports = {
testEnvironment: "jsdom",
};
这告诉 Jest 使用jsdom作为我们的 DOM 实现。
接下来更新您的package.json
文件:
package.json
{
"scripts": {
"rollup": "rollup -c",
"test": "jest"
},
...
}
现在我们可以运行我们的测试:
npm run test
不幸的是,我们会得到一个错误!错误是在遇到我们的 JSX 代码时。如果你还记得我们使用 Typescript 来处理 JSX 和我们的 rollup 配置,还有一个用于 rollup 的 Typescript 插件来教它如何做到这一点。不幸的是,我们没有为 Jest 准备这样的设置。
我们将需要安装Babel来处理我们的 JSX 转换。我们还需要安装一个 Jest 插件babel-jest
,它会告诉 Jest 使用 Babel!现在让我们安装它们,连同 Babel 插件来处理我们的 Typescript 和 React 代码。所有这些的总集合看起来像:
npm install @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript babel-jest --save-dev
现在我们在项目的根目录中创建 Babel 配置文件,它告诉 Babel 使用我们刚刚安装的所有这些插件:
babel.config.js
module.exports = {
presets: [
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-typescript",
],
};
现在我们应该能够运行我们的测试了npm run test
……但是……还有一个问题!
你会得到一个错误说的import
了的.css
文件不被理解。这是有道理的,因为我们再次postcss
为 rollup配置了一个插件来处理这个问题,但我们没有为 Jest 做这样的事情。
最后一步是安装一个名为identity-obj-proxy的包。这样做是允许您配置 Jest 以将任何类型的导入视为通用对象。因此,我们将使用 CSS 文件来执行此操作,以免出现错误。
npm install identity-obj-proxy --save-dev
我们需要更新我们的 Jest 配置 tp 包括moduleNameMapper
属性。我们还在那里添加了less
和scss
以备您以后扩展项目以使用它们:
jest.config.js
module.exports = {
testEnvironment: "jsdom",
moduleNameMapper: {
".(css|less|scss)$": "identity-obj-proxy",
},
};
现在最后,如果你已经跟进到这一点,你可以运行:
npm run test
您将获得成功的测试!
添加故事书
Storybook 是一种用于可视化站点/应用程序之外的 UI 组件的工具。它非常适合对组件的不同视觉状态进行原型设计和测试,以确保它们按设计的方式工作,而无需在屏幕上放置其他不相关组件的额外开销。
它还为您提供了一种在库项目中处理组件时查看和使用组件的简单方法,而无需构建不必要的测试页面来显示它们。
初始化 Storybook 非常简单。要设置它并自动配置它,我们只需运行以下命令:
npx sb init
与我们迄今为止添加的其他一些工具不同,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!",
};
一开始这可能有点让人不知所措,但是当您一点一点地阅读它时,您应该会发现它相当简单。
- 该默认导出定义了按钮会出现在故事书。我选择ReactComponentLibrary作为一个简单的名称来将我们的自定义组件与示例分开组合在一起。
- 该模板确定哪个部件实际上被渲染,并且该默认ARGS /道具适用于它。
- 的Template.bind对象是组件的实例或示例的状态。因此,在实际项目中,您可能会遇到“LargeButton”和“SmallButton”之类的东西。因为我们的按钮总是很大,所以我只是用了一个例子来测试带有两个不同标签的按钮。
如果您查看您的package.json
文件,您会看到 Storybook 已经添加了一个storybook
和storybook-build
脚本。第一个将在本地托管 Storybook 应用程序,以便快速轻松地进行测试。第二个将构建一个可以轻松托管在远程服务器上的静态 HTML/JS 包,以便您团队的所有成员都可以试用您的组件。
现在让我们运行:
npm run storybook
编辑:由于缺少依赖项,您可能会遇到错误。如果发生这种情况,有几种解决方案。
第一种是手动安装这些依赖项。例如react-dom
。这并不理想,因为您的项目本身不应该依赖于这些库,因此没有必要包含它们,因为它们包含在 Storybook 的对等依赖项中,例如here。
如果您只是运行一个新npm install
命令,它将安装peerDependencies
您正在使用的所有库。在运行之前,您可能需要删除您的package-lock.json
和node_modules
目录。它们将在您全新安装后自动重新生成。
解决与库之间重叠和缺失依赖项相关的问题可能很棘手。保持耐心并确保阅读您的错误消息!)
如果一切顺利,您将看到一个友好的界面,让您可以实时浏览示例组件以及您自己的自定义按钮。在它们之间单击以查看您创建的不同状态。
关于 Storybook 有很多要了解的内容,请务必通读文档。
包起来
您现在应该对如何创建自己的 React 组件库有了很好的了解。这样做不仅可以教你很多关于 Javascript 包管理生态系统如何工作的知识,而且它可以是一种很好的方式,使你可以通过一个简单的命令轻松获得跨多个项目使用的代码。
请查看我的一些其他学习教程。如果您觉得有任何帮助,请随时发表评论或问题并与他人分享:
- 运行本地 Web 服务器
- ESLint
- 更漂亮
- 通天塔
- 反应和 JSX
- Webpack:基础知识
- Webpack:加载器、优化和包分析
- Webpack:DevServer、React 和 Typescript
如需更多此类教程,请在 Twitter 上关注我@eagleson_alex