有太多文章讲如何发布npm包,大部分是写一个极其简单的片段,设置好npmrc,就能发布,而真实场景中,我们往往是要做一个被他人引用的库,那么我们到底该怎样配置这个库的项目呢,这里我用了lerna作为monorepo的工具,用了parcel做示例,一步一步介绍如何工程化地开发一个react组件库。
还不了解monorepo和lerna的朋友们,我在这篇文章介绍了如何初始化一个monorepo的多包项目(开发组件库之前,如何初始化Monorepo仓库)。
1. 首先将react框架加入到monorepo中
1-1. 首先新建react文件夹和package.json
上篇文章我们已经了解到不同的包都会放在packages目录下,我们在里面新建react文件夹。

并准备好package.json:
json
{
"name": "@free/react",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
1-2. 安装react
在react目录下运行:npm install react @types/react typescript --include=dev,我们可以看到刚才的package.json也相应地更新了。

1-3. 设置typescript
在react目录下新建tsconfig.json,并写入基本的配置,请额外注意别漏了include和exclude这两项。
json
{
"compilerOptions": {
"target": "ES5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": false,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": false,
"noImplicitAny": false,
"strictNullChecks": false,
"noImplicitReturns": false,
"noUnusedLocals": false,
"noUnusedParameters": false,
"jsx": "react-jsx",
"declaration": true,
},
"include": [
"src",
],
"exclude": [
"node_modules",
"lib"
]
}
1-4. 新建源代码文件结构
因为我正在做的是组件库封装,我在这篇文章《封装表单元素,应该定义哪些状态或属性?》里将表单组件分为了typeable,selectable等,这里我从最简单的input出发,原生input属于typeable,因此我新建的文件结构如下:

1-5. 新建一个TextInput组件
在Text.tsx中,我写了一个TextInput组件:
jsx
import React from 'react';
const Text = () => {
return (
<input type="text" value="test"/>
);
}
export default Text;
别忘了更新index.ts:
ts
export { default } from './Text';
export * from './Text';
1-5. 集中导出组件
在packages/react/src/index.ts中,集中导出所有的组件,这里只有Text.ts,那我只导出这一个:
ts
import Text from './fields/typeable/text';
export {
Text
}
这样,我们在一个monorepo的项目中引入了react,并创建了一个react组件库。
2. 编译发布这个react组件库
之前读过这本书:《现代JavaScript库开发》,我还写了一部分笔记:《现代JavaScript库开发》笔记 - 模块规范和构建。这本书讲最好采用rollup.js作为开发库的编译工具。
2-1. 安装rollup
在react目录下运行npm install rollup rollup-plugin-typescript2 --dev,其中rollup-plugin-typescript2是用来编译ts文件的。然后加上rollup.config.mjs,这样写:
mjs
import TS from 'rollup-plugin-typescript2';
export default {
input: [
'src/index.ts',
'src/fields/typeable/text/index.ts',
],
output: {
dir: "lib", // 编译时会按照library的文件结构编译
format: "es", // 用ES module导出文件
sourcemap: true // 方便debug
},
plugins: [Ts()], // 使用typescript插件,如果你不用ts也可以不写
preserveModules: true // 保留module和源文件一致的文件结构
preserveModulesRoot: 'packages/react/src',
external: ['react']
}
这样rollup就配置好了。
2-2. 更新package.json
在react下的package.json也加上一个指令:
json
"scripts": {
"build": "rollup -c"
}
这样,如果我们在根目录下运行npm run build,build完后,我们可以在react目录下看到一个lib文件夹,里面就是编译好的文件:

如果我们希望使用者也能够单独引用Text.tsx组件,则在rollup.config.mjs的"input"中加上"src/fields/typeable/text/index.ts"。这样build后我们就能得出这些文件:

当我们开发完了一个组件时,在发布前,我们想看看这个组件是否能按我们所期待的方式工作,这里我推荐设置一个playground。
3. 设置react playground
在根目录下新建一个名为"playgrounds"的文件夹,package.json里的"workspaces"中的"packages"加上"playgrounds"。
json
// root/package.json
"workspaces": [
"packages/*",
"playgrounds"
],
然后在playgrounds里同样的新建一个react项目。

3-1. 安装ts版本的react和parcel
我们安装parcel,这样我们就可以创建一个示例页面来引用我们的包。选择parcel是因为它很好设置。
按以下命令安装:npm install react @types/react react-dom @types/react-dom typescript parcel-bundler --include-dev。 再按照react常规文件结构我们创建react项目。

再在index.html中写个最基础的结构:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Playground React</title>
</head>
<body>
<div id="root"></div>
<script src="index.tsx"></script>
</body>
</html>
3-2 设置playgrounds中的react app
也是和之前的流程类似,加入tsconfig,这个从packages里的react文件夹里复制过来就行。
在package.json中,我们再加上一些scripts:
playgrounds/react/package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "parcel src/index.html -p 3000" // <=== 你可以自己定义用什么端口
},
3-3 注意
在root/packages/react/package.json中,有这么一段:"main": "index.js",这是用来定义入口文件的,所以这里要改成:"main": "lib/index.js"。这样导出的取的就是lib下面的index.js文件。
3-4 在example react中引入我们的包
现在我们在playgrounds/react中就可以这样引入我们之前写的包了:import { Text } from '@free/react'。整段代码可以这样写:
jsx
// playgrounds/react/src/index.tsx
import React from 'react'
import ReactDOM from 'react-dom'
import { Text } from '@free/react'; // <=== 这里引入我们写好的包
ReactDOM.render(
<Text />,
document.querySelector('#root')
);
然后在这个react项目的根目录下,跑命令启动:npm run dev。然后我们就可以在页面上看到这个组件啦。

4 发布npm
最后我们在打包后的packages/react目录下运行常规的发布npm的命令:npm publish,就可以发布啦。
结尾
这只是一个开头,关于如何完整地,系统地开发一个前端框架,后面我会持续更新下去。