大家好,我是心锁,一枚23届准毕业生。
近期我正在尝试完成所谓的「拐角轮播」,目前稍有收获(虽然拐角部分完成的并不是很好)
在完成之后,本来是打算写一下「CornerSwiper」的实现思路的,但是后来在打包组件库时却屡屡翻车,最终怒火之下我决定先把我在打包与发布React组件库时踩的所有坑进行一个总结,并尝试输出一份能让读者在十分钟内完成react组件库构建与发布的实践指南。
经过调研,发现一件严肃的事情,即国内的组件库构建实战分别两个特点,要么是特别详细但是需要大量配置,要么特别容易上手但是巨多坑,所以经过不断进行技术方案的调研,最终选用了如下的技术方案进行组件库搭建。
同时,相关demo已以分支的形式放在了github上,可以拉下来尝试https://github.com/GrinZero/magic-design-react/tree/demo
经过笔者实践,得出一份在React组件库构建时的对比
对比 | dumi | storybook |
---|---|---|
国内教程 | 多🔥 | 少 |
安装难度 | 简单🔥(开箱即用) | 一般 |
组件文档编写 | 简单🔥(基本全React原生语法) | 麻烦(需要额外学习mdx以及storybook自己实现的语法) |
编译体验 | 快🔥(MFSU,号称比VIte还快) | 一般 |
UI | 我喜欢❤️ | 真觉得不好看 |
对比过程我就不说了,都是泪,这是被我废弃的storybook仓库https://github.com/GrinZero/magic-design
mdx的编写麻烦不说,丢失代码提示、学习新的并且看起来很无语的API,都着实让我心累。
(而且UI也让我不甚喜欢)
故此,一觉醒来我换成了dumi,半个小时即迁移完毕~
常见的打包方式包括webpack、rollup
以及vite(基于rollup)
,更老的则包括gulp
;
而我最后选择了father
,准确来说是father 2.x
,一方面是dumi
官方推荐,一方面是上边各个打包方式均有一个特点,就是需要从头配置,并且大多包含大量的依赖项、配置项,有一定的学习成本,比较复杂。
而在father4.rc
与father 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
打包出来的产物和下图类似
这种打包形式目前已知的问题包括
_app.tsx
文件中导入样式import "xxx.less"
这种语法,这本身就是不对的而通过father2.x
配置后,理想的导出结果类似于:
专门为CSS开了一个单节,因为发现在组件库的设计中,CSS其实是很重要的一部分。
我们知道,CSS样式覆盖的问题一直是CSS的"特性",社区中为了解决css样式冲突也有不少方案,其中基本包括css modules、css in js、BEM规范、原子类
这些解决方案。
对于前两者,很遗憾,对于组件库来讲我们并不推荐使用,原因是会给使用者的样式覆盖造成一定的困惹。
所以我对比tailwindcss
和windicss
后选择了tailwindcss
一方面,经过调研,常规认知中tailwindcss
的卡顿特性,随着“JIT”模式的推出,已经消失得一干二净。
另一方面,调研发现,windicss
实现的技术是所谓的virtual
包,虚拟npm包。
而很遗憾,上文提到的MFSU功能并不支持虚拟npm包(见这个issue https://github.com/umijs/umi/issues/7303)
而且即便通过webpack集成进去,我们也会发现,由于father2
和father4.rc
并不是很支持配置webpack插件,在此基础上,配置windicss
会很艰难。
(不要寄希望于postcss,虚拟包就是不支持)
说完了技术方案的选型,我们进入方案的实践(踩坑)阶段。
当然如果你不想重新走一遍,可以直接拉取https://github.com/GrinZero/magic-design-react/tree/demo
基于我们需要搭建组件文档的需求,这里我们只需要运行
yarn create @umijs/dumi-lib
那么我们可以得到如图的目录
需要注意的是,默认使用father4.rc
来进行构建,我们需要手动将版本修改为更稳定的2.x,我这里是使用2.30.21
值得注意的是,由于tailwindcss3依赖于postcss8,而umi目前为止还是在使用postcss7,所以我们必须安装兼容postcss7版本的tailwindcss
yarn add tailwindcss@npm:@tailwindcss/postcss7-compat
在进行下一步之前,请先执行
yarn install
完成依赖安装
docs/index.md
移动到src/index.md
,原因在后边说。可以看到,这是umi脚手架生成的初始配置
而我们需要把它改成这样
在这一步,需要安装一个依赖(autoprefixer其实默认配置了,但是我还是加上了)
yarn add postcss-import@^11 -D
而这里是完整的.umirc.ts的代码
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'),
],
});
我们分点解释一下这里的各个配置原因:
显式配置mode,确保创建的是文档模式组件库
可以看到,base和publicPath均被配置了我的工程名称
base: '/magic-design-react',
publicPath: '/magic-design-react/',
目的是为了适应在github pages上进行部署,github pages带有项目名后缀,
而另外一点值得注意的是outputPath,我们填写的是docs
,原因就在上图的红圈处,github page目前看到提供的选项就是「root」和「docs」,所以我们需要打包到docs
文件夹。
我们发现,这里新增了两个插件
其中,tailwindcss
负责为我们引入tailwindcss,而postcss-import
则是做了一个把css文件中的@import
语法进行bundle的操作。
踩了多次坑之后,我参考了tdesign-react
和antd
这两个组件库,发现这两个组件库都是通过直接引入bundle样式来引入组件库样式的。并不存在css按需引入
我们知道,脚手架生成的基本没啥配置
而现在我们需要改成这样:
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'],
},
};
解释一下各个选项~
重要,把less转成css,而不是保持less,当然也可以配置同时打包,和antd
一样方面用户配置样式
umi
保持一致,这也是为什么我通过postcss来引入tailwindcss,我们尽量保证生产和开发一样。建立src/global.css
,并在src/index.ts
引入
必须引入,原因是,umi
虽然约定式自动引入,但是对于father
是无效的。
同时我建议在global.css
中配置需要全局引入的css
postcss-import
会把这份文件打包在一起
注意!我们需要手动开启tailwindcss的jit模式
最后一步,我们需要配置package.json
这是最简单却重要的一步
一方面,我们需要设定module
,typings
以及files
,前两者指定了npm包的入口\npm包的类型入口,而files则指定了我们发包时需要上传的文件(图中代表需要上传dist)
另一方面,很重要的一步是设定peerDependencies
并指定react框架的版本,详情看https://reactjs.org/warnings/invalid-hook-call-warning.html#duplicate-react
而指定React版本,实际上是最大的坑,非常建议能做兼容就兼容,否则问题很大。
我们通过||
来兼容不同版本的React,否则我们可能会遇到类似这样的报错
TypeError: Cannot read properties of null (reading 'useRef')
npm包的发布也是有坑的~
我们的包名是magic-design-react
,在这种情况下,不会遇到什么坑。
但如果我们发的包名是@xxx/magic-design-react
,我们就有必要做两件事。
创建组织是免费的,尽管创建就是了
写在根目录的.npmrc
,记得不要跟着git仓库上传了
如果想免费发布一个带有作用域的npm包,我们需要使用npm publish --access public
来发布
我默认读者对于npm发布是比较理解的,所以不细说,只讲了一些小坑
我们使用main分支下的docs
文件夹来支持github page
在根目录下运行docs:build命令就会进行打包
yarn docs:build
打包产物在docs
文件夹,注意该文件夹需要跟着git上传,不可以配置.gitignore
然后直接通过docs:deploy
发布即可
(平时使用npm run deploy
就行,不需要拆开)
那么跟着本文跑完,我们已然完成了一个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)
额外附带一份vite的rollup我的最佳实践 # vite的rollup打包 这里是
rollupOptions
部分,用于打包出组件库
后边这里则生成.d.ts
,提供类型支持
在vite的plugins中配置