如何在monorepo中,发布react组件库?

有太多文章讲如何发布npm包,大部分是写一个极其简单的片段,设置好npmrc,就能发布,而真实场景中,我们往往是要做一个被他人引用的库,那么我们到底该怎样配置这个库的项目呢,这里我用了lerna作为monorepo的工具,用了parcel做示例,一步一步介绍如何工程化地开发一个react组件库。

还不了解monorepolerna的朋友们,我在这篇文章介绍了如何初始化一个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,并写入基本的配置,请额外注意别漏了includeexclude这两项。

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. 新建源代码文件结构

因为我正在做的是组件库封装,我在这篇文章《封装表单元素,应该定义哪些状态或属性?》里将表单组件分为了typeableselectable等,这里我从最简单的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,就可以发布啦。

结尾

这只是一个开头,关于如何完整地,系统地开发一个前端框架,后面我会持续更新下去。

相关推荐
我不吃饼干3 小时前
在 React 中实现倒计时功能会有什么坑
前端·react.js
haaaaaaarry5 小时前
Element Plus常见基础组件(二)
开发语言·前端·javascript
PyHaVolask5 小时前
HTML 表单进阶:用户体验优化与实战应用
前端·javascript·html·用户体验
花菜会噎住7 小时前
Vue3核心语法进阶(computed与监听)
前端·javascript·vue.js
I'mxx7 小时前
【vue(2)插槽】
javascript·vue.js
花菜会噎住7 小时前
Vue3核心语法基础
前端·javascript·vue.js·前端框架
全宝7 小时前
echarts5实现地图过渡动画
前端·javascript·echarts
啃火龙果的兔子8 小时前
解决 Node.js 托管 React 静态资源的跨域问题
前端·react.js·前端框架
sophie旭9 小时前
《深入浅出react》总结之 10.7 scheduler 异步调度原理
前端·react.js·源码
吃饭睡觉打豆豆嘛9 小时前
彻底搞懂前端路由:从 Hash 到 History 的演进与实践
前端·javascript