引入
本来是准备写设计低代码的,但在建设低代码组件库时,因为尝试了市面上主流的各种创建项目的方式来打包库,也就此碰到了一系列的问题,所以今天就让我们来尝试设计一个多元化的组件库吧✌️
最后结论
-
使用的项目管理工具最后选择为rush+pnpm
-
我们最后产出的组件库特征:
- 经过各种尝试后,选择了以React为主技术栈
- 在开发时使用源码引入、生产时使用产物引入的模式
- css都打在组件库包中
- 在没有使用storybook的场景下,每一个组件都有一个自己的showcase页面,showcase和build设置不同的entry
组件技术栈选择
vue-cli+vue
-
最前提!!:打lib包时,vue-cli只支持生成common、umd的包,不支持esm,目前没有解决方案,已提issue github.com/vuejs/vue-c… (但没人理
- 由于我们开发和生产引用的分别是源码和产物,在tsconfig中开启allowSyntheticDefaultImports可以做umd和esm的引入兼容www.typescriptlang.org/zh/tsconfig…
- 就算做了以上处理,在消费侧引入basic-components时还是会报错,所以最后没有使用vue-cli来打包库
-
因为选择了自己写showcase页面,所以需要做一下start和build的entry的不同配置,vue-cli的entry只有一个,而且vue-cli-service不可以动态指定entry,所以需要在vue.config.js中根据参数改变entry
-
打lib包时,vue-cli默认会把css单独抽出一个文件,这样的话需要basic-components进行css样式的引入,比较难做css的tree-shaking,我们可以采用antd的方式,写一个babel插件将引入basic-components的代码转换为动态引入,也可以简单的将样式inline进入打出来的js文件,在vue.config.js中增加
css: { extract: false } -
vue-cli打包库时无法简单支持同时打包出.d.ts文件,根据 github.com/vuejs/vue-c… 这位同学的思路,进行了一些在当前版本上的配置修改
configureWebpack: (config) => {
// 多entry
config.entry =
process.env.NODE_ENV === "development"
? ["./src/show-case/main.ts"]
: ["./src/components/index.vue"];
if (process.env.NODE_ENV === "production") {
config.module.rules.forEach((rule) => {
if (rule.use) {
let idx = rule.use.findIndex((w) => w.loader.includes("thread-loader"));
if (idx !== -1) {
rule.use.splice(idx, 1)
}
}
});
}
},
// 打出.d.ts
chainWebpack: (config) => {
if (process.env.NODE_ENV === "production") {
config.module
.rule("ts")
.use("ts-loader")
.loader("ts-loader")
.tap((opts) => {
opts.transpileOnly = false;
opts.happyPackMode = false;
return opts;
});
}
},
整体来说vue-cli对于打包库的支持没有很好
❌
vite+vue
- vite指定dev和prod不同entry更方便一些,dev只要保证index.html在root的根目录下即可,prod则需要配置vite.config.ts文件build中的lib
- vite也无法直接生成.d.ts定义,目前vite也没有支持的想法github.com/vitejs/vite… ,在这个issue下提供了一个同学写的插件 vite-pligin-dts,试下来是可以生成.d.ts的
- vite中有默认的postcss配置,但是不支持extract的配置,如果强行再配置postcss插件会报错,所以最后使用了style in js的设计来写组件样式
整体来说,比起vue-cli还是好用一些
✅
vite+react
- 由于React17的@types/react/jsx-runtime的类型定义文件写的有问题,没有把类型export出来
导致打包时会报ts定义错误
在创建项目的后几天React18.0就推出了,于是整体升级了React的版本后,发现18版本的@types/react/jsx-runtime也并没有修复这个问题
最后选择了不引入react/jsx-runtime,使用classic的方式来绕开这个问题,在vite.config.js中配置参数,可以用React16及以前的jsx转义方法
- 生成.d.ts方面因为是React的组件代码,直接使用rollup-plugin-typescript2,在vite.config.js中配置即可(但经过实际尝试后发现,rollup-plugin-typescript2的钩子执行时机会在vite编译之后,拿到的是已经压缩擦掉ts的文件,所以打咩,使用这个插件还是可以的vite-pligin-dts)
- css同上
如果@types/react能修那个定义问题,整体来说还是很顺畅的
✅
create-react-app + react
create-react-app没有直接支持打包lib,需要eject出配置或者用babel打,太麻烦了弃
❌
tsdx+react
对于build react lib来说非常无敌的好用
默认打包lib + 默认自带定义文件,开箱即用,我给100个赞
(这里本来很好奇为啥tsdx没有碰到上面React17、18的问题,看了一下tsdx也没有使用新的react-jsx-runtime模式,和我们使用vite一样绕开了这个问题
另一个小问题是tsdx默认是基于rollup、babel对我们的组件进行打包的,没有使用esbuild等更高性能的打包工具,因此对于大型的组件,可以考虑自定义rollup的配置提升性能。
✅ ✅ ✅
这边尝试这几种脚手架方式来创建,直接用rollup或者webpack写就没有包括在其中啦,如果大家有什么在开发lib时体验感更好的脚手架也可以推荐给我们~~
basic-components + platform-consumer设计
组件代码引入是选择源码引入还是产物引入?
我们设计了basic-components的包,进行组件的导入和统一导出
组件的导入有两种形式:
-
基础组件不用单个发包,basic-components使用源码引入的方式引入
- 优势:简单、不用管理多个包、所有的包的依赖项可以通过peerDependencies进行统一、代码风格一致
- 劣势:限制了开发基础组件的技术、第三方服务想要接入basic-components无法灵活做裁剪
-
每个基础组件需要单个发包,basic-components使用产物引入的方式引入
- 优势:每个基础组件都可以有自己的框架+打包方式
- 劣势:代码风格不一致、容易触发类似React同一应用下不可有两个大版本差异的限制
我们最后选用的方式是单个发包+产物引用,但是在开发时如果采用这种方式会需要在每个包(基础组件包+basic-components)中做watch产出最新的产物,会比较麻烦和占用cpu
所以在开发时我们还是使用的类似源码引用的方式,在每个基础组件和basic-components的package.json中都自定义了一个entry,它的指向文件是源码,在basic-components的消费方处判断是否是开发环境,如果是,使用mainFields更改入口文件
这种方式利用了monorepo的link模式,使消费侧在开发时可以找到组件源码
使用vite作为platform-consumer
vite会自动的将commonjs包转换成esmodule(类似lodash),但是如果引的包是monorepo link的话,vite会将其解析成source
在我们的case下,开发环境将包作为source进入,所以在生产环境要配置一下
pnpm+rush选择原因
-
如果使用的是monorepo的形式,还是尽可能使用yarn或者pnpm,我们碰到了一种case,有个package需要使用cross-env的bin,npm的workspaces虽然可以安装这个包,但是不会在单个package中增加bin,yarn可以做到
- 将lerna.json中的npmClient设为yarn就可以啦
- publish相关
-
而将npm替换为yarn后,由于需要配置vite中的插件,tsdx依赖的rollup为1.x版本,而vite依赖的rollup为2.x版本,但是yarn将1.x版本的rollup做了依赖提升,导致vite中报了rollup版本不匹配的错,虽然看了tsdx官方已经将rollup版本提了上去只是没有发版本后,可以用yarn resolution解决,但还是将yarn换成了pnpm。并且由于lerna不支持pnpm,所以切成了rush+pnpm。
- 切到pnpm之后,有一些本来靠依赖提升使用的包需要每个用到的项目手动增加依赖
还没有做的
- UT
- Web Component组件