五分钟学会发布React组件到 NPM 包

2,206 阅读7分钟

前言

最近在做的项目需要实现一个可拖拽、可设置大小、并且在指定范围内才可以移动的需求,在翻了一些组件后,发现需要三样都满足,并且能满足项目定制化需要的插件几乎没有~,于是自己动手实现了该插件,整体效果如下:

drag-resizable.gif

在写完之后,突然想到可以放到 NPM 库中,以后维护该组件更加方便版本管理,于是趁五一长假,终于将包发布上去了~

在发布 NPM 时踩了一些坑,比如脚手架打包后 TS 提示消失、比如 NPM 的官方源换成了 HTTPS 让发包失败等等因素导致整个过程非常不顺。

为了避免自己未来继续踩同样的坑,或许同时还能够帮助别人节省一点时间,于是才有了这篇博客。

  • 如果你希望未来想用 React+TypeScript 完成一个组件库并发布到 NPM

  • 如果你想了解我在发布时踩了哪些坑

那么这篇博客适合你,阅读时长大约五分钟。

下面是正题。

脚手架工具

采用 React+TypeScript 实现的组件需要借助打包工具将代码打包好才能够被引入,我在查了一些资料后发现有以下三款工具:

这三款工具都预设好了打包工具,比如 dumi 用的 webpack、tsdx 用的 parcel,而且也基本实现了 0 配置,我们只需要根据官方文档就可以很快地完成组件编写、用例测试、打包等环节。

其中create-react-library已经很少维护了,所以我没试过它。

tsdx 和 dumi 带给我的体验差不太多,而且发布最重要的其实是写文档,两款工具都有对应的文档工具,tsdx 内置的是 storybook,我写的第一个组件库就是用的它,但是实在太难配插件了~

tsdx 的问题

一开始我用的 tsdx,但是发现几个问题:

问题一:

我的组件在测试用例时,无法触发 ts 的提示,然后我发现它打包后的声明文件中需要将:

import react from 'react'

修改成:

import * as react from 'react

我的组件才能够有类型提示。

问题二:

tsdx 中修改组件源代码后,需要手动再次打包,然后删掉 example 文件夹内的 node_modules文件以及.parcel_cache文件夹生成新的 dist 目录,才会有效果。换言之它的 watch模式貌似有问题?

dumi 的问题

于是我试了一下 dumi,dumi写文档的方式对我而言要比 tsdx 的 storybook 方便,不过它也有一点缺陷:

即使用了官方推荐的 ts-in-markdown插件,在写组件 demo 时,也是没有api智能提示的,只能提示有没有语法错误之类的。

这个缺陷就使得写 demo 时没有办法知道当别人用你的组件时,有没有代码自动提示。(自动提示props真的很重要啊!!)

不过最终我找到了解决这个问题的方法,方法在【本地测试组件】。

我最终选择 dumi

从结果来说使用 dumi 打包完的组件还是有类型提示的,不需要我手动改 dist 目录。

我的组件文档首页目前是这样的:

image.png

基本满足了我的需求,所以下面讲讲如何使用 dumi 的发布过程。

一些微不足道的经验

如何使用 dumi 这里就不多说,看官方文档就好,我在这里讲一下我遇到的问题以及解决方法:

  1. 当组件的 props 类型与外部引入的组件的 props 类型合并后,写 API 时会默认将所有props 的api都展示出来。

    这一点我们需要在.umirc.ts中配置 apiParser:

  apiParser: {
    propFilter: {
      // 是否忽略从 node_modules 继承的属性,默认值为 false
      skipNodeModules: true,
      // 需要忽略的属性名列表,默认为空数组
      skipPropsWithName: [],
      // 是否忽略没有文档说明的属性,默认值为 false
      skipPropsWithoutDoc: false,
    },
  },
  1. 当配置publicPathbase后,如果 logofavicon用的本地图片,一定要在前面加上publicPathbase的路径。
  favicon: '/react-drag-resizable/lightbulb.png',
  logo: '/react-drag-resizable/lightbulb.png',
  base: '/react-drag-resizable/',
  publicPath: '/react-drag-resizable/',

否则部署后logo 和 favicon 的图片会引入失败。(这点在开发环境下发现不了)

  1. dumi官方给的 github-pages-action 自动部署的yml文件有问题 官方给的 yml 文件是这样写的:
name: github pages


on:
  push:
    branches:
      - master # default branch 这里有问题,需要改成 main
      - main # 这才是正确的


jobs:
  deploy:
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@v2
      - run: npm install
      - run: npm run docs:build
      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./docs-dist
  1. 官方给的自定义导航、分组和标题写法有问题

官方写法如下:

---
title: 自定义页面名称
nav:
  path: /自定义导航路由
  title: 自定义导航名称
  order: 控制导航顺序,数字越小越靠前,默认以路径长度和字典序排序
group:
  path: /自定义分组路由,注意,分组路由 = 导航路由 + 自己
  title: 自定义分组名称
  order: 控制分组顺序,数字越小越靠前,默认以路径长度和字典序排序
---

有效的写法:

---
title: 自定义页面名称
nav:
path: /自定义导航路由
title: 自定义导航名称
order: 控制导航顺序,数字越小越靠前,默认以路径长度和字典序排序
group:
path: /自定义分组路由,注意,分组路由 = 导航路由 + 自己
title: 自定义分组名称
order: 控制分组顺序,数字越小越靠前,默认以路径长度和字典序排序
---

看出差别了吗?😏

  1. 已经将必要的模块导出,但写 docs 时引入该模块依然报错 比如我已经在 src/index.ts中导出组件内的类型声明和导出组件了:
export type { RectProps, DragResizableBoxProps } from './react-drag-resizable';

export { default as DragResizableBox } from './react-drag-resizable';

但是在写 doc 时,正常引入RectProps,还是报错了。

image.png

执行npm run build打包出一个 dist 文件夹,然后重启 vscode 解决。

使用github-actions自动化部署

我们可以使用 github-actions 自动化将 dumi 的组件文档部署到 github-pages 中,实现步骤如下:

  1. 在 github 建一个代码仓库

  2. 到github 的个人主页申请一个 token

image.png

  1. 选择token 的 note和过期时间

image.png

image.png

  1. 权限选择

image.png

  1. 最后生成 token,将token 复制到你的个人仓库的 setting/secrets

image.png

我在这里使用的是 ACCESS_TOKEN,也可以自己写,不过这里的名字需要跟后面的 yml 文件保持一致。

  1. 在你的项目根目录中创建目录和 yml 文件
.github
└── workflows
    └── gh-pages.yml

yml文件中这样写

name: github pages

on:
  push:
    branches:
      - main # default branch

jobs:
  deploy:
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@v2
      - run: npm install
      - run: npm run docs:build
      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.ACCESS_TOKEN }}
          publish_dir: ./docs-dist

注意最后的secrets.ACCESS_TOKEN里面的ACCESS_TOKEN就是你在 github 仓库中设置的token名字。

  1. 在项目的package.json中配置homepage字段
  "homepage": "https://[github username].github.io/[github repository]",
  1. 将代码推送到远程仓库,actions 会自动下载依赖并打包

  2. 最后打开你配置的 homepage就可以看到部署好的文档,我的文档地址是这样的

    18888628835.github.io/react-drag-…

本地测试组件

在发包前最好再测试一下。由于写 docs 时提示不对,我们没办法做到全面的测试。

所以这里运行 npm link命令创建软链接到全局的 node_modules 下。你会得到一串字符

/Users/xxx/.nvm/versions/node/v12.14.0/lib/node_modules/xxx -> /Users/xxx/Job/xxx

然后在项目根目录创建一个 test 的目录,用来写测试案例。

在 test 中创建example.tsx,然后在 test 目录下运行npm link xxx将测试目录的组件库xxxxxxx链接到全局,注意这里的xxx修改成你自己的。

然后我们就可以在example.tsx里面引入我们的组件了。

import { DragResizableBox } from 'react-drag-resizable';

然后你就可以在这里测试一下有没有代码提示之类的诸如docs无法做到的测试。

发布到 NPM

  1. www.npmjs.com/ 中注册一个账号

  2. package.json中配置一些内容

  "private": false,// 表示非私有
  "name": "react-drag-resizable",// 包名
  "description": "React component, which can be used to drag and resize, with location limits",// 描述
  "author": "qiuyanxi",//作者
  "license": "MIT",// 许可
  "version": "1.1.0",// 版本
  "keywords": [
    "React",
    "drag",
    "resize"
  ],// 关键字
  "homepage": "https://18888628835.github.io/react-drag-resizable",// 主页地址
  "repository": {
    "type": "git",
    "url": "https://github.com/18888628835/react-drag-resizable.git"
  },// 仓库地址
  "files":["dist"],// 要发哪些文件到 NPM 包中
  "main": "dist/index.js",// 引入的来源
  "module": "dist/index.esm.js",
  "typings": "dist/index.d.ts",// 类型

上面的配置是我收集到的,如果不是非常特殊的情况,我们只需要将 name、description、author、version、keywords、homepage、repository修改成自己的就可以了,其他可以不动。

  1. 修改 README 一个 NPM 包,好的 README 能够快速让用户知道这个包能够干什么,如何使用等。

这里有一个 github 高 star 的项目,教人怎么写 README,并提供了模板。

地址在这:standard-readme中文

同时我们可能还需要徽章,这里有一个徽章生成的工具网站,可以根据 NPM 或者 github 来生成徽章,我们只需要拷贝到 README 就可以生成徽章

徽章生成:shields.io/

在 README 中的徽章生成示例代码:

[![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)

会变成这样:standard-readme compliant

我们只需要拷贝代码,并将生成的链接覆盖掉这串代码上的链接就行

  1. 在项目中输入命令行发布 登陆 npm,并根据提示输入注册时的 username、password, email
$ npm login

npm镜像源,记得还原为官方,官方的源已经换成了 https 协议了

$ npm config set registry https://registry.npmjs.org/

设置完了也可以再看一眼

$ npm config get registry

执行 npm 打包,此时会出来 dist 文件

$ npm run build

一般来说我们只需要将 dist 传到 npm 就行了,不需要传源码,这时候你可能需要再看一眼 package.json中的 files 字段是不是设置正确了

 "files":["dist"],// 要发哪些文件到 NPM 包中

发包

$ npm publish

后面更新包的代码时,需要同步更新包的版本,这个字段在 package.json

  "version": "1.1.0",// 版本

每次都需要跟上一次不一样,否则会发包失败。

同时发包时,也可以修改其他package.json的内容,但不要改name噢。

最后

推广一下本人长期维护的 github 博客

1.从学习到总结,记录前端重要知识点,涉及 Javascript 深入、HTTP 协议、数据结构和算法、浏览器原理、ES6、前端技巧等内容。

2.在 README 中有目录可对应查找相关知识点。

如果对您有帮助,欢迎star、follow。

完结,撒花🌹✿✿ヽ(°▽°)ノ✿

参考

# 是时候搭建你们团队的 UI 组件库了

# GitHub Actions 入门教程-阮一峰

# 使用TypeScript + React发布组件到Npm