阅读 1279

基于 CRA 快速搭建 React 组件库

本章篇幅较长,尬不多说,造轮子开始~~


1. 初始化项目 create-react-app + TS

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

参考:create-react-app.dev/docs/adding…

2. 支持编译 Sass

CRA 默认不支持 Sass 预处理器,需要安装 node-sass

npm install node-sass --save

支持 sass 之后,接下来可以为组件库添加一些样式管理,比如定义基础组件库色板变量,

// 基础色板
$blue: #0d6efd !default;
$indigo: #6610f2 !default;
$purple: #6f42c1 !default;
$pink: #d63384 !default;
$red: #dc3545 !default;
$orange: #fd7e14 !default;
$yellow: #fadb14 !default;
$green: #52c41a !default;
$teal: #20c997 !default;
$cyan: #17a2b8 !default;
复制代码

添加 reset 全局样式,可参考 normalize.css 库,它提供了跨浏览器的高度一致性。

3. 编写 Button 组件

下面简单列举开发组件几个常用的点,至于怎么开发出一个合格好用的组件,还需要日积月累、慢慢摸索~

  1. 组件 className 属性一般支持 btn, btn-lg, btn-primary 等不同样式管理,推荐使用 classnames 插件进行样式名管理, 配置 classNames :

npm i classnames --save npm install --save @types/classnames

  1. 通过 Button.defaultProps 为添加 props 赋默认值

  2. ButtonHTMLAttributesbutton 所有原生属性类型,可以通过定义自定义属性类型和 button 所有原生属性类型,让用户使用组件时获得更好的属性类型提示。

Button 组件参考实现 ⬇️

// Button.tsx
import React, { FC, ButtonHTMLAttributes, AnchorHTMLAttributes } from "react";
import classNames from "classnames";

export type ButtonSize = "lg" | "sm";

export type ButtonType = "primary" | "default" | "danger" | "link";

interface BaseButtonProps {
  className?: string;
  /**设置 Button 的禁用 */
  disabled?: boolean;
  /**设置 Button 的尺寸 */
  size?: ButtonSize;
  /**设置 Button 的类型 */
  btnType?: ButtonType;
  children: React.ReactNode;
  href?: string;
}
// ButtonHTMLAttributes 是button所有原生属性类型
type NativeButtonProps = BaseButtonProps & ButtonHTMLAttributes<HTMLElement>;
// a 链接原生属性
type AnchorButtonProps = BaseButtonProps & AnchorHTMLAttributes<HTMLElement>;
// TS Utility Types : Partial 属性可选,并不是都有的
export type ButtonProps = Partial<NativeButtonProps & AnchorButtonProps>;

export const Button: FC<ButtonProps> = (props) => {
  const {
    btnType,
    className, // 用户自定义className
    disabled,
    size,
    children,
    href,
    ...restProps //  包含了其他的所有原生属性
  } = props;

  // 配置 classNames : btn, btn-lg, btn-primary
  // disable说明:button 默认支持disabled属性;但 a 链接disable属性就要通过样式来控制了,所以添加到classname里
  const classes = classNames("btn", className, {
    [`btn-${btnType}`]: btnType,
    [`btn-${size}`]: size,
    disabled: btnType === "link" && disabled,
  });

  // button 包括 link类型和其他类型
  if (btnType === "link" && href) {
    return (
      <a className={classes} href={href} {...restProps}>
        {children}
      </a>
    );
  } else {
    return (
      <button className={classes} disabled={disabled} {...restProps}>
        {children}
      </button>
    );
  }
};

Button.defaultProps = {
  disabled: false,
  btnType: "default",
};

export default Button;
复制代码

4. 使用 Jest 测试框架

Create React App 中,Jest 已经能够开箱即用,且包含许多实用的默认配置。

从 package.json 中可以看到 CRA 已内置了相关测试包:

{
  "scripts": {
    "test": "react-scripts test"
  },
  "dependencies": {
    "@testing-library/jest-dom": "^5.11.4",
    "@testing-library/user-event": "^12.1.10",
    "@testing-library/react": "^11.2.5"
  }
}
复制代码

1. 编写测试用例

// Button.test.tsx
import React from "react";
import { render } from "@testing-library/react";
import Button from "./Button";

test("my test", () => {
  const wrapper = render(<Button>按钮</Button>);
  const ele = wrapper.queryByText("按钮");
  expect(ele).toBeTruthy();
});
复制代码

2. 执行 npm run test

此时会先运行 setupTests.ts 文件,然后跑一遍测试用例。

参考:

create-react-app.dev/docs/runnin…

www.jestjs.cn/

testing-library.com/

5. 组件库文档开发与部署

本文使用文档生成工具 docz 进行开发。docz Demo 参考 create-react-app-ts Doc

1. 安装 npm install docz

2. 配置 scripts 脚本

{
  "scripts": {
    "docz:dev": "docz dev",
    "docz:build": "docz build",
    "docz:serve": "docz build && docz serve"
  }
}
复制代码

3. 为 Button 组件添加文档说明

新建 Button.mdx 文件(按照模板规范编辑即可) 注意需要引入全局样式 index.scss.

---
name: Button
menu: Components
---

import { Playground, Props } from "docz";
import { Button } from "./Button";
import "../../style/index.scss";

# Button

## Properties

<Props of={Button} />

## Basic usage

<Playground>
  <Button btnType="primary"> primary button </Button>
</Playground>

## Using different kinds

<Playground>
  <Button btnType="danger"> danger button </Button>
  <Button btnType="link" href="https://google.com">
    link button
  </Button>
</Playground>
复制代码

4. 添加配置文件 doczrc.js

主要用来支持编译 TS

// doczrc.js
export default {
  typescript: true,
  files: ["./src/**/*.mdx"], // 指定生成文档的文件
};
复制代码

参考:docz Project Configuration

5. 编译 SCSS

此时运行的本地组件库文档没法正常展示样式,需要配置编译 SCSS 文件,参考 Using docz with CSS Preprocessors

  • npm install --save gatsby-plugin-sass
  • 配置 gatsby-config.js 文件
//gatsby-config.js
module.exports = {
  plugins: ["gatsby-plugin-sass"],
};
复制代码

6. 本地预览与打包

执行 npm run docz:dev,此时本地文档就可以正常跑起来啦 (没有做美观调整,组件库样式简单粗暴)

一般本地运行报错之后,会存在缓存文件,需要删除本地.docz 文件夹,重新运行 npm run docz:dev 即可。

下面是代码目录结构

接下来就是如何将打包后的组件库文档,部署到远端服务器,并进行在线浏览

7. 使用 Netlify 一键部署

本项目采用 Netlify 一个可以用来做静态网站的持续集成与持续部署的工具。只需设置一次,以后每次我们提交代码的时候,Netlify 会自动帮我们部署最新的代码。

  • 首先执行 npm run docz:build,生成打包后的文档默认在 .docz/dist 目录下,提交代码。
  • 然后就是 Netlify 的相关配置,主要就是配置需要部署的代码 git 地址、部署前自动执行的脚本 npm run docz:build 以及需要配置待部署的文件目录位置 .docz/dist

Netlify 的部署配置工作是十分简单的,基本按照提示就可以完全配置好。可以参考这篇文章使用 netlify 部署你的前端应用

Deploy log 日志可以很清晰的看出 Netlify 的构建过程 :

Installing dependencies -> Build command from Netlify app --> Deploy site --> Build script success

一键 deploy 成功后,直接点击域名链接即可愉快的访问我们的组件库在线文档啦!传送门 🚪

6. 组件库打包

部署完组件库在线文档后,接下来就是如何将我们的组件库进行打包与发布了,这里主要使用 TS 进行编译配置。

1. 创建组件库入口文件

// index.tsx
export { default as Button } from "./components/Button";
复制代码

2. 编写 tsconfig 配置文件及打包

  • 新建 tsconfig.build.json 文件进行 TS 编译配置文件
  • 配置 script 脚本: "build-ts":"tsc -p tsconfig.build.json"
  • 执行 npm run build-ts 进行打包
{
  "compilerOptions": {
    "outDir": "dist",
    "module": "esnext",
    "target": "es5",
    "declaration": true,
    "jsx": "react",
    "moduleResolution": "Node",
    "allowSyntheticDefaultImports": true
  },
  "include": ["src"],
  "exclude": ["src/**/*.test.tsx", "src/setupTests.ts"]
}
复制代码

发现此时 scss 文件并没有被打包,接下来使用 node-sass 进行编译

  • 添加 script 脚本:"build-css": "node-sass ./src/styles/index.scss ./dist/index.css"

  • 编译前需要删除老的 dist 文件夹。 Linux 下可以使用 rm -rf dist,但不兼容 windows。CRA 已经内置了 rimraf 插件,增加删除 script 脚本 "clean": "rimraf ./dist" , 参考如下:

{
  "scripts": {
    "start": "react-scripts start",
    "build": "npm run clean && npm run build-ts && npm run build-css", // 顺序执行
    "clean": "rimraf ./dist",
    "build-ts": "tsc -p tsconfig.build.json",
    "build-css": "node-sass ./src/style/index.scss ./dist/index.css"
  }
}
复制代码

执行 npm run build, 控制台可以看到三条命令依次执行。

打包后目录结构如下

3. 配置组件库入口文件

  • package.json 中新增字段
{
  "main": "dist/index.js",
  "module": "dist/index.js",
  "types": "dist/index.d.ts"
}
复制代码

7. 使用 npm link 本地测试组件库

在本地开发 npm 模块的时候,我们可以使用 npm link 命令,将 npm 模块链接到对应的运行项目中去,方便地对模块进行调试和测试

  1. 本地新建一个 demo 项目 test-component-library 用于测试组件库的使用
  2. 当前组件库 my-component-library 下(需要被 link 的包)执行:
npm link
// success : /usr/local/lib/node_modules/my-component-library -> /Users/当前组件库项目路径/my-component-library
// 全局 node_modulus 下会创建一个软链接 ---> 链接到当前组件库项目路径
复制代码
  1. demo 项目(需要 link 本地包的测试项目)下执行:
npm link my-component-library
// success : /Users/当前测试项目路径/test-component-library/node_modules/my-component-library -> /usr/local/lib/node_modules/my-component-library -> /Users/liyang86/当前组件库项目路径/my-component-library

// 本地 test 项目中 node_modules/my-component-library 创建软链接 ---> 链接到全局环境 node_modules/my-component-library 作为中转 ---> 又链接到当前组件库项目路径
复制代码

此时 test 项目 node_modules 中已有 my-component-library 文件夹了!并且修改组件库内容时,demo 项目中 link 的组件包也会实时更新哦。

  1. 引入组件及全局样式
// test-component-library 下
import { Button } from "my-component-library";
import "my-component-library/dist/index.css";

<Button btnType="primary" size="lg">
  Button
</Button>;
复制代码

运行一下测试项目,Button 组件已经可以正常使用啦。

8. 发布 npm

本地测试组件库通过后,接下来就是最后一步,发布组件库到 npm!

1. 配置 package.json 常用字段

  • description: "React Component Lib",
  • author: "my",
  • private: false,
  • license: "MIT",
  • keywords: [ "Component","UI","React"],
  • homepage: "github.com/sdyz/my-com…",
  • repository: { "type": "git", "url": "github.com/sdyz/my-com…" },
  • files: ["dist"], dist 文件夹下内容即待发布文件

即使不配置 ignore 或者 files 也会被发布的文件:package.json、README.md、changelog.md、license

2. 提交发布前检查

通过命令行钩子函数的方式进行验证,包括测试用例验证和 lint 代码格式检查

  • ESLint 检查
    • --ext 选项 指定 ESLint 在指定的目录下查找 JavaScript 文件时要使用的文件扩展名
    • 配置脚本 "lint": "eslint --ext js,ts,tsx src --max-warnings 5"
    • 执行 npm run lint 进行测试
  • Test 检查
    • npm run test 方式会 watch,但不会返回结果,下面进行改进 参考:create-react-app.dev/docs/runnin…
    • cross-env 跨平台设置环境变量 npm install --save-dev cross-env
    • 配置脚本 "test:nowatch": "cross-env CI=true react-scripts test",
    • 执行 npm run test:nowatch 测试成功。假如测试用例不通过,会中断退出运行。
  • 增加代码提交校验工具 Husky
 "husky": {
    "hooks": {
      "pre-commit": "npm run test:nowatch && npm run lint"
    }
  },
复制代码

3. 配置 publish 脚本

  • prepublish 发布前的钩子函数
  • 配置脚本 "prepublish": "npm run test:nowatch && npm run lint && npm run build"

4. 发布

  • npm whoami 检测是否登录 npm
  • npm adduser 未登录的话进行登录/注册
  • npm config lsnrm ls 查看当前 registry 信息(需要使用默认的 npm 源)
  • 执行 npm publish 成功发布!

9. 组件库使用

npm install @sdyz/my-component-library

import { Button } from "@sdyz/my-component-library";
import "@sdyz/my-component-library/dist/index.css";

<Button btnType="primary" size="lg">
  Button
</Button>;
复制代码

大功告成!!撒花 ✿✿ ヽ(°▽°)ノ ✿ ✌️✌️✌️

参考

JSDoc 注释规范

组件库包地址

文章分类
前端
文章标签