目标
使用 react + typescript 写组件库,发布至 npm,支持按需加载,有文档,单元测试和 CI
项目地址
本文主要记录开发过程中遇到的一些配置问题,以及如何解决的。可能不是最好的解决方案,但总归还是解决了吧,如果有人遇到相同问题的可以参考一下。
webpack 配置
我使用的是 create-react-app 来生成项目,后面觉得非常不合适,因为打包库和打包应用的配置是有很多差别的,并且生成的配置很多,反而增加了修改的难度。而且现在我认为打包库最好用 rollup,原因和按需加载有关
yarn eject
运行 yarn eject
修改配置
去掉多余的的配置
把 WorkboxWebpackPlugin 干掉,这个是用于 PWA 的,会导致最终打包的文件多一个 service-worker.js 和一个 precache-manifest.[hash].js
把 optimization 下的 splitChunks 和 runtimeChunk 干掉,因为要生成一个 index.js
umd 打包
output: {
libraryTarget: 'umd',
}
按需加载
组件库要提供按需加载的功能,有两种方式。
-
一种是将组件库的每个组件都打包至单独的目录下,比如
// 增加 entry.js const path = require("path"); const fs = require("fs"); function getEntries() { function isDir(dir) { return fs.lstatSync(dir).isDirectory(); } const entries = { index: path.join(__dirname, `../src/index.tsx`), }; const dir = path.join(__dirname, "../src/components"); const files = fs.readdirSync(dir); files.forEach((file) => { const absolutePath = path.join(dir, file); if (isDir(absolutePath)) { entries[file] = path.join( __dirname, `../src/components/${file}/index.tsx` ); } }); return entries; } const entryies = getEntries(); module.exports = entryies;
// webpack.config.js const entryies = require('./entries'); { ... entry: entryies, filename: (chunkData) => { return chunkData.chunk.name === 'index' ? 'lib/[name].js': 'lib/components/[name]/index.js'; }, ... }
这样,使用者就可以直接引用单个组件而不是整个的 index.js 了。并且他们也可以使用 babel-plugin-import 或者其他插件来简化写法,只不过需要加点配置。使用者如何配置可见文档
- 另一种方式就是将组件库以 es6 模块的方式导出,并在 package.json 增加 module 字段,那么使用者用的 webpack 或者其他打包工具就可以自动识别,去加载组件库导出的 es 模块的代码了,也可以支持 tree-shaking 了。另外,有写文章说到 tree-shaking 可能没用什么的,我觉得有没有用是一回事,支不支持是另一回事了。目前我是没找到 webpack 可以方便的支持导出 es6 模块的方法,rollup 是天生就支持的。所以我个人觉得还是用 rollup 打包组件库比较好。
另外,MiniCssExtractPlugin 里的 filename 字段也改一下
使用 svg-sprite-loader
这是因为写 Icon 组件时要用到 svg,这个插件可以将多个 svg 文件整合到一个 svg
元素下,每个 svg 文件对应一个 symbol
元素,并且把整个 svg
元素插入到页面中。这样我们想使用某个 svg 元素时用相应的 symbol
就可以了。
npm 配置
private
private: true
,否则发布不了
main
"main": "./build/lib/index.js"
, 指定使用者默认引入的文件
type
"types": "./build/lib/index.d.ts"
,指定使用者引入的 TS 类型文件
files
"files": [
"/build/lib/**/*"
],
需要发布的目录
TypeScript 配置
配置别名
一般我们在 webpack 里会配置别名,所以使用了 TS 之后也要配置相应别名
"baseUrl": "./",
"paths": {
"@components/*": ["src/components/*"],
"@utils/*": ["src/common/utils/*"]
},
这个 baseUrl 一定要有
自动生成类型定义文件
"isolatedModules": false,
"declaration": true,
"declarationDir": "build/lib",
"emitDeclarationOnly": true,
这里有一个巨坑,按照文档的说法,首先要把 onEmit: true
删掉,然后只要配置了 "declaration": true,
应该就可以自动生成 .d.ts
文件了。但是我这里不行,最初我修改 npm script 的 build 命令为 "build": "node scripts/build.js & npx tsc -p tsconfig.json --emitDeclarationOnly"
。当时确实是解决了,但是在之后的 circleCI 环节又出了问题,circleCI 上最后 build 出来的文件里只有 .d.ts
文件了,其他的所有 .js
.css
文件都消失了。但是在我本地是没问题的。经过多次测试发现用 &&
就可以了。虽然我知道 &
是并行执行 &&
是串行执行,但是还是不清楚为什么我本地没问题但是 circleCI 上就不行了。。。然后我把 emitDeclarationOnly
放到 tsconfig.js
里了,发现也不影响 node scripts/build.js
打包出组件代码,所以最终配置就是如上所示。package.json
里配置 "build": "node scripts/build.js && npx tsc -p tsconfig.json"
以上就是打包组件库最重要的三个配置,接下来还有文档,单元测试以及 CI