前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从文档开发框架到package.json,带你走一轮React组件库构建与发布

从文档开发框架到package.json,带你走一轮React组件库构建与发布

作者头像
源心锁
发布2022-09-21 14:46:57
3.9K0
发布2022-09-21 14:46:57
举报
文章被收录于专栏:前端魔法指南前端魔法指南

1 前言

大家好,我是心锁,一枚23届准毕业生。

近期我正在尝试完成所谓的「拐角轮播」,目前稍有收获(虽然拐角部分完成的并不是很好)

image-20220824235700796
image-20220824235700796

在完成之后,本来是打算写一下「CornerSwiper」的实现思路的,但是后来在打包组件库时却屡屡翻车,最终怒火之下我决定先把我在打包与发布React组件库时踩的所有坑进行一个总结,并尝试输出一份能让读者在十分钟内完成react组件库构建与发布的实践指南。

2 技术方案

经过调研,发现一件严肃的事情,即国内的组件库构建实战分别两个特点,要么是特别详细但是需要大量配置,要么特别容易上手但是巨多坑,所以经过不断进行技术方案的调研,最终选用了如下的技术方案进行组件库搭建。

  • dumi - 负责组件开发及组件文档生成,基于umi框架
  • father 2.x - 负责组件库的构建,即打包的过程
  • tailwindcss 3.x - 负责提供原子类,优化开发体验
  • ts+less - 组件库经典方案
  • github-pages发布组件库文档

同时,相关demo已以分支的形式放在了github上,可以拉下来尝试https://github.com/GrinZero/magic-design-react/tree/demo

2.1 核心方案对比

2.1.1 dumi对比storybook

经过笔者实践,得出一份在React组件库构建时的对比

对比

dumi

storybook

国内教程

多🔥

安装难度

简单🔥(开箱即用)

一般

组件文档编写

简单🔥(基本全React原生语法)

麻烦(需要额外学习mdx以及storybook自己实现的语法)

编译体验

快🔥(MFSU,号称比VIte还快)

一般

UI

我喜欢❤️

真觉得不好看

对比过程我就不说了,都是泪,这是被我废弃的storybook仓库https://github.com/GrinZero/magic-design

mdx的编写麻烦不说,丢失代码提示、学习新的并且看起来很无语的API,都着实让我心累。

  • dumi的文档编写
image-20220825012204503
image-20220825012204503
image-20220825005000466
image-20220825005000466
  • storybook的文档编写
image-20220825012318208
image-20220825012318208

(而且UI也让我不甚喜欢)

image-20220825004754459
image-20220825004754459

故此,一觉醒来我换成了dumi,半个小时即迁移完毕~

2.1.2 构建库(打包)方式对比

常见的打包方式包括webpack、rollup以及vite(基于rollup),更老的则包括gulp

而我最后选择了father,准确来说是father 2.x,一方面是dumi官方推荐,一方面是上边各个打包方式均有一个特点,就是需要从头配置,并且大多包含大量的依赖项、配置项,有一定的学习成本,比较复杂。

image-20220825015907272
image-20220825015907272

而在father4.rcfather 2.x之间我之所以选择了2.x而不是dumi官方脚手架@umijs/create-dumi-lib 安装下来的4.rc,主要原因在于father4.rc真的非常多坑:

对比

father2.x

father4.rc(father next)

less打包

可以选择性转换成css与bundle🔥

不支持😈

自定义rollup插件

支持添加rollup插件🔥

仅umd模式下支持

cssModules

rollup模式下支持

不支持

postCSS支持

通过extraPostCSSPlugins或者extraRollupPlugins都可以

仅umd模式下支持

会发现,father4比较还是处于rc阶段的产物,太多功能是不支持的,想在father4.rc集成原子类要踩的坑实在太多,通过father4打包出来的产物和下图类似

image-20220825020037451
image-20220825020037451

这种打包形式目前已知的问题包括

  • 在NextJs无法使用,因为Next是约定式导入样式文件,仅允许在_app.tsx文件中导入样式
  • 正常项目未配置less-loader无法使用,怎么会有组件库打包继续使用import "xxx.less"这种语法,这本身就是不对的

而通过father2.x配置后,理想的导出结果类似于:

image-20220825020439694
image-20220825020439694

2.2 CSS方案选择

专门为CSS开了一个单节,因为发现在组件库的设计中,CSS其实是很重要的一部分。

我们知道,CSS样式覆盖的问题一直是CSS的"特性",社区中为了解决css样式冲突也有不少方案,其中基本包括css modules、css in js、BEM规范、原子类这些解决方案。

对于前两者,很遗憾,对于组件库来讲我们并不推荐使用,原因是会给使用者的样式覆盖造成一定的困惹。

所以我对比tailwindcsswindicss后选择了tailwindcss

8E0B48BD4AA1E478A961D2C5EC0ECDDB
8E0B48BD4AA1E478A961D2C5EC0ECDDB

一方面,经过调研,常规认知中tailwindcss的卡顿特性,随着“JIT”模式的推出,已经消失得一干二净。

另一方面,调研发现,windicss实现的技术是所谓的virtual包,虚拟npm包。

而很遗憾,上文提到的MFSU功能并不支持虚拟npm包(见这个issue https://github.com/umijs/umi/issues/7303)

image-20220825103919732
image-20220825103919732
image-20220825103947107
image-20220825103947107

而且即便通过webpack集成进去,我们也会发现,由于father2father4.rc并不是很支持配置webpack插件,在此基础上,配置windicss会很艰难。

(不要寄希望于postcss,虚拟包就是不支持)

3 方案实现

说完了技术方案的选型,我们进入方案的实践(踩坑)阶段。

当然如果你不想重新走一遍,可以直接拉取https://github.com/GrinZero/magic-design-react/tree/demo

3.1 初始化项目

3.1.1 使用dumi脚手架初始化项目

基于我们需要搭建组件文档的需求,这里我们只需要运行

代码语言:javascript
复制
yarn create @umijs/dumi-lib

那么我们可以得到如图的目录

image-20220825122938348
image-20220825122938348

3.1.2 使用father2而不是father4

需要注意的是,默认使用father4.rc来进行构建,我们需要手动将版本修改为更稳定的2.x,我这里是使用2.30.21

image-20220825123204157
image-20220825123204157

3.1.3 安装tailwindcss

值得注意的是,由于tailwindcss3依赖于postcss8,而umi目前为止还是在使用postcss7,所以我们必须安装兼容postcss7版本的tailwindcss

代码语言:javascript
复制
yarn add tailwindcss@npm:@tailwindcss/postcss7-compat
image-20220825123619698
image-20220825123619698

3.2 构建打包

在进行下一步之前,请先执行

代码语言:javascript
复制
yarn install

完成依赖安装

3.2.1 文件迁移

  • docs/index.md移动到src/index.md,原因在后边说。

3.2.2 .umirc.ts

可以看到,这是umi脚手架生成的初始配置

image-20220825124448591
image-20220825124448591

而我们需要把它改成这样

image-20220825124546407
image-20220825124546407

在这一步,需要安装一个依赖(autoprefixer其实默认配置了,但是我还是加上了)

代码语言:javascript
复制
yarn add postcss-import@^11 -D

而这里是完整的.umirc.ts的代码

代码语言:javascript
复制
import { defineConfig } from 'dumi';

export default defineConfig({
    mode: 'doc',
    base: '/magic-design-react',
    publicPath: '/magic-design-react/',
    exportStatic: {},
  outputPath: 'docs',
    title: 'magic-design-react',
    favicon: 'https://user-images.githubusercontent.com/9554297/83762004-a0761b00-a6a9-11ea-83b4-9c8ff721d4b8.png',
    logo: 'https://user-images.githubusercontent.com/9554297/83762004-a0761b00-a6a9-11ea-83b4-9c8ff721d4b8.png',
    mfsu: {},
    theme: {
        '@primary-color': '#7FA1F7',
    },
    extraPostCSSPlugins: [
        require('postcss-import'),
        require('tailwindcss')({
            config: './tailwind.config.js',
        }),
        require('autoprefixer'),
    ],
});

我们分点解释一下这里的各个配置原因:

#1 mode:'doc'

显式配置mode,确保创建的是文档模式组件库

#2 base、publicPath、outputPath

可以看到,base和publicPath均被配置了我的工程名称

image-20220825125038239
image-20220825125038239
代码语言:javascript
复制
    base: '/magic-design-react',
    publicPath: '/magic-design-react/',

目的是为了适应在github pages上进行部署,github pages带有项目名后缀,

image-20220825125246599
image-20220825125246599

而另外一点值得注意的是outputPath,我们填写的是docs,原因就在上图的红圈处,github page目前看到提供的选项就是「root」和「docs」,所以我们需要打包到docs文件夹。

#3 extraPostCSSPlugins

我们发现,这里新增了两个插件

image-20220825163530686
image-20220825163530686

其中,tailwindcss负责为我们引入tailwindcss,而postcss-import则是做了一个把css文件中的@import语法进行bundle的操作。

  • 为什么需要postcss-import?

踩了多次坑之后,我参考了tdesign-reactantd这两个组件库,发现这两个组件库都是通过直接引入bundle样式来引入组件库样式的。并不存在css按需引入

3.2.3 .fatherrc.ts

我们知道,脚手架生成的基本没啥配置

image-20220825164748275
image-20220825164748275

而现在我们需要改成这样:

image-20220825165622548
image-20220825165622548
代码语言:javascript
复制
import resolve from '@rollup/plugin-node-resolve';
import postcss from 'rollup-plugin-postcss';

export default {
    esm: 'rollup',
    lessInRollupMode: {
        javascriptEnabled: true,
    },
    entry: ['./src/index.ts'],
    extraRollupPlugins: [
        resolve(),
        postcss({
            extract: true,
            plugins: [
                require('postcss-import'),
                require('tailwindcss')({
                    config: './tailwind.config.js',
                }),
                require('autoprefixer'),
            ],
        }),
    ],
    autoprefixer: {
        browsers: ['ie>9', 'Safari >= 6'],
    },
};

解释一下各个选项~

#1 lessInRollupMode

重要,把less转成css,而不是保持less,当然也可以配置同时打包,和antd一样方面用户配置样式

#2 extraRollupPlugins
  • resolve插件
image-20220825170029933
image-20220825170029933
  • postcss插件
    • 开启extract,输出CSS
    • 配置plugins,和umi保持一致,这也是为什么我通过postcss来引入tailwindcss,我们尽量保证生产和开发一样。

3.2.4 全局CSS的配置

建立src/global.css,并在src/index.ts引入

image-20220825163011189
image-20220825163011189

必须引入,原因是,umi虽然约定式自动引入,但是对于father无效的。

同时我建议在global.css中配置需要全局引入的css

image-20220825170609445
image-20220825170609445

postcss-import会把这份文件打包在一起

image-20220825171613204
image-20220825171613204

注意!我们需要手动开启tailwindcss的jit模式

image.png
image.png

3.2.5 配置package.json

最后一步,我们需要配置package.json

这是最简单却重要的一步

一方面,我们需要设定module,typings以及files,前两者指定了npm包的入口\npm包的类型入口,而files则指定了我们发包时需要上传的文件(图中代表需要上传dist)

image-20220825171804185
image-20220825171804185

另一方面,很重要的一步是设定peerDependencies指定react框架的版本,详情看https://reactjs.org/warnings/invalid-hook-call-warning.html#duplicate-react

image-20220825172237776
image-20220825172237776

而指定React版本,实际上是最大的坑,非常建议能做兼容就兼容,否则问题很大。

image-20220825172333300
image-20220825172333300

我们通过||来兼容不同版本的React,否则我们可能会遇到类似这样的报错

代码语言:javascript
复制
TypeError: Cannot read properties of null (reading 'useRef')

3.3 发布npm

npm包的发布也是有坑的~

我们的包名是magic-design-react,在这种情况下,不会遇到什么坑。

但如果我们发的包名是@xxx/magic-design-react,我们就有必要做两件事。

3.3.1 创建组织

创建组织是免费的,尽管创建就是了

image-20220825172950609
image-20220825172950609

3.3.2 配置token

写在根目录的.npmrc,记得不要跟着git仓库上传了

image-20220825173234668
image-20220825173234668

3.3.3 发布命令变化

如果想免费发布一个带有作用域的npm包,我们需要使用npm publish --access public 来发布

我默认读者对于npm发布是比较理解的,所以不细说,只讲了一些小坑

3.4 发布Github Page

3.4.1 github page配置

我们使用main分支下的docs文件夹来支持github page

image-20220825174026925
image-20220825174026925

3.4.2 打包发布

在根目录下运行docs:build命令就会进行打包

代码语言:javascript
复制
yarn docs:build

打包产物在docs文件夹,注意该文件夹需要跟着git上传,不可以配置.gitignore

image-20220825174323269
image-20220825174323269

然后直接通过docs:deploy发布即可

image-20220825174401890
image-20220825174401890

(平时使用npm run deploy就行,不需要拆开)

4 总结

那么跟着本文跑完,我们已然完成了一个esm标准的React组件库

再次抛一个deom链接,可以直接拉下来跑https://github.com/GrinZero/magic-design-react/tree/demo

(当然如果想从头跑一圈也行)

然后就是,如果觉得好用给点个Star呗~我看大家反馈来确定要不要抽成一个简单的脚手架。

最后,亲来参观下我的组件库吧https://grinzero.github.io/magic-design-react/swiper/corner-swiper

(也可以给组件库点个star哦https://github.com/GrinZero/magic-design-react)

image-20220825174534670
image-20220825174534670
image-20220825174603397
image-20220825174603397

额外附带一份vite的rollup我的最佳实践 # vite的rollup打包 这里是rollupOptions部分,用于打包出组件库

image-20220825013348543
image-20220825013348543

后边这里则生成.d.ts,提供类型支持

image-20220825013712052
image-20220825013712052

在vite的plugins中配置

image-20220825013736353
image-20220825013736353
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-08-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 前言
  • 2 技术方案
    • 2.1 核心方案对比
      • 2.1.1 dumi对比storybook
      • 2.1.2 构建库(打包)方式对比
    • 2.2 CSS方案选择
    • 3 方案实现
      • 3.1 初始化项目
        • 3.1.1 使用dumi脚手架初始化项目
        • 3.1.2 使用father2而不是father4
        • 3.1.3 安装tailwindcss
      • 3.2 构建打包
        • 3.2.1 文件迁移
        • 3.2.2 .umirc.ts
        • 3.2.3 .fatherrc.ts
        • 3.2.4 全局CSS的配置
        • 3.2.5 配置package.json
      • 3.3 发布npm
        • 3.3.1 创建组织
        • 3.3.2 配置token
        • 3.3.3 发布命令变化
      • 3.4 发布Github Page
        • 3.4.1 github page配置
        • 3.4.2 打包发布
    • 4 总结
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档