有太多文章讲如何发布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
,就可以发布啦。
结尾
这只是一个开头,关于如何完整地,系统地开发一个前端框架,后面我会持续更新下去。