使用 React、TypeScript 和 Rollup 设置组件库
介绍
组件库正变得越来越流行,尤其是在拥有多个产品和团队的组织中。一些组织甚至专门组建团队来维护组件库。最终目标可能是构建一个设计系统,其中包含我们深思熟虑的原则和实践。但是,一个好的设计系统需要数月甚至数年的研究以及一个专门的团队,而这对于许多组织来说难以承受。谷歌的Material Design和 Atlassian 的设计系统就是其中两个优秀的例子。对于大多数团队来说,组件库是一个很好的起点。组件库是常用组件的集合,可以帮助实现应用程序之间的一致性。我们可以从简单的组件开始,例如 `<div>` button、 `<div>` 等inputs,modal然后逐步添加更多组件。
让我们尝试使用 React、Typescript 和 Rollup 从头开始构建一个简单的组件库,并在此过程中学习一些东西。
初始化项目
我们首先创建一个目录并初始化一个npm名为的项目。react-lib
mkdir react-lib
cd react-lib
npm init
您可以填写问题,也可以传递-y标志以使用默认值进行初始化。现在package.json我们的项目中已经有了这个文件。
既然我们要用到 ` reactand` 和typescript`,我们可以添加这些依赖项。
npm i -D react typescript @types/react
由于我们将以库的形式发布此项目,因此所有包都将列在 `<package_name>` 下devDependencies。此外,使用此库的应用程序将自带 React,我们无需单独打包 React。所以,我们将添加 `<package_name>`react作为 `<package_name> `。现在peerDependency我们的 ` <package_name>` 看起来是这样的。package.json
添加组件
我更喜欢将组件组织在src/components文件夹内,每个组件都有自己的文件夹。例如,如果我们有一个Button组件,就会有一个名为 `<button>` 的文件夹,其中Button包含src/components所有与按钮相关的文件,例如Button.tsx` Button.css<button-name>`、` <button-name>`、`<button-name>` 等Button.types.ts,以及一个index.ts用于导出该组件的文件。
此外,还有几个索引文件用于导出内容。一个是项目的主要入口点,位于 `<path>` src/index.ts,另一个用于导出所有组件,位于 `<path>` src/components/index.ts。按钮组件的文件夹结构如下所示。
按钮组件
现在,我们来添加组件的代码Button。我这里使用一个非常简单的组件,因为这并不是我们目前真正需要关注的重点。
Button.tsx
Button.css
Button.types.ts
Button/index.ts
现在我们有了Button组件,我们可以从 components 和 src 中导出它。
TypeScript 配置
我们已经添加了组件,现在为了构建库,我们需要配置 TypeScript。我们已经安装了 TypeScript 依赖项,现在需要添加它tsconfig.json。我们可以通过以下方式完成此操作:
npx tsc --init
这将创建一个tsconfig.json文件,其中大部分可用选项都被注释掉了。我主要使用默认设置,只做了一些细微的修改。
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"jsx": "react",
"sourceMap": true,
"outDir": "dist",
"strict": true,
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
}
}
让我们在代码中添加一个构建脚本package.json来测试一下。
"scripts": {
"build": "tsc"
},
如果我们运行该命令npm run build,应该会看到一个dist文件夹,其中包含所有已转译为 js 文件的所有 ts 文件。你会注意到,其中没有 css 文件dist,它们也没有被我们的 ts 编译器打包。让我们使用Rollup来实现这一点。
汇总配置
我们将使用Rollup作为打包工具。那么,让我们来安装它。
npm i -D rollup
插件
Rollup 拥有一个插件系统,我们可以通过它来指定打包过程中需要执行的所有任务。我们需要以下插件:
@rollup/plugin-node-resolve- 解决第三方依赖关系node_modules@rollup/plugin-commonjs- 将commonjs模块转换为 ES6@rollup/plugin-typescript- 将我们的 TypeScript 代码转译成 JSrollup-plugin-peer-deps-external- 防止捆绑销售peerDependenciesrollup-plugin-postcss- 处理我们的 CSSrollup-plugin-terser- 为了缩小我们的软件包
让我们来安装这些插件。
npm i -D @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-typescript rollup-plugin-peer-deps-external rollup-plugin-postcss rollup-plugin-terser
rollup.config.js
下一步是添加rollup.config.js文件。我们所有的汇总配置都存放在这里。
我们的库的入口点是该src/index.ts文件,我们会将库打包成两种commonjs格式es modules。如果使用此库的应用程序支持 esmodules,则会使用 格式的esm构建;否则,cjs将使用 格式的构建。
rollup.config.js
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import { terser } from 'rollup-plugin-terser';
import external from 'rollup-plugin-peer-deps-external';
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,
name: 'react-lib'
},
{
file: packageJson.module,
format: 'esm',
sourcemap: true
}
],
plugins: [
external(),
resolve(),
commonjs(),
typescript({ tsconfig: './tsconfig.json' }),
postcss(),
terser()
]
}
我们已经为我们的构建定义了input和值。outputcjsesm
把所有东西整合起来
请注意,我们已经在`from`中指定了该file选项。接下来,让我们定义这两个值。outputpackage.jsonpackage.json
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
现在我们已经配置好了 Rollup,我们可以在构建脚本中使用它来package.json代替tsc之前的命令。
"build": "rollup -c"
如果我们npm run build现在运行,可以看到已经dist创建了一个文件夹,其中包含了我们的库输出。
该cjs文件夹包含commonjs捆绑包,该esm文件夹包含现代esmodules捆绑包。
我们拥有自己的库,现在可以将其发布到 npm 注册表,也可以在本地与其他应用程序一起使用。
测试一下
我们可以使用npm pack或npm link在本地测试我们的库。
捆绑类型
dist运行后,如果你查看我们的文件夹npm run build,会发现我们没有打包类型。使用 TypeScript 的优势在于,代码编辑器可以识别类型并提供智能感知和静态类型检查,这非常有用。它还能减少频繁查阅文档的需求。
tsconfig.json我们需要在生成类型时添加一些选项。
"declaration": true,
"declarationDir": "types",
"emitDeclarationOnly": true
添加此项将在我们的文件夹中添加一个 types 文件夹cjs,并esm在其他文件夹中添加一个dist。
我们可以通过提供一个包含库中使用的所有类型的单个文件来进一步改进这一点。为此,我们将使用一个名为rollup-plugin-dts的 Rollup 插件,它会读取我们所有的.d.ts文件并输出一个单独的类型文件。
npm i -D rollup-plugin-dts
我们可以添加另一个入口点rollup.config.js来添加我们的类型配置。
{
input: 'dist/esm/types/index.d.ts',
output: [{ file: 'dist/index.d.ts', format: "esm" }],
external: [/\.css$/],
plugins: [dts()],
},
这样做会index.d.ts从我们的 esm 包中获取文件,解析所有类型文件,并index.d.ts在我们的dist文件夹中生成一个类型文件。
现在我们可以types在列表中添加一个条目了。package.json
"types": "dist/index.d.ts"
整个rollup.config.js画面现在看起来是这样的。
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import { terser } from 'rollup-plugin-terser';
import external from 'rollup-plugin-peer-deps-external';
import postcss from 'rollup-plugin-postcss';
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,
name: 'react-ts-lib'
},
{
file: packageJson.module,
format: 'esm',
sourcemap: true
}
],
plugins: [
external(),
resolve(),
commonjs(),
typescript({ tsconfig: './tsconfig.json' }),
postcss(),
terser()
],
},
{
input: 'dist/esm/types/index.d.ts',
output: [{ file: 'dist/index.d.ts', format: "esm" }],
external: [/\.css$/],
plugins: [dts()],
},
]
现在,如果我们在其他项目中使用我们的库,代码编辑器可以识别类型并提供智能感知和类型检查。
结论
这绝非搭建组件库的完整或完美方法。这只是一个入门的基本设置,旨在帮助用户了解打包过程。下一步是添加测试和工具,例如Storybook或Styleguidist。
源代码可以在这里找到:react-ts-lib
感谢阅读!
祝好!






