如何在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 18 核心特性详解:Props、State 与 Context 的深度实践
react.js·前端框架
itslife3 分钟前
实现 render 函数 - 初始化更新队列
前端·react.js·前端框架
baozj4 分钟前
一次表单数据复用引发的 Bug:理解 Vue 中的 data 为何是函数
前端·javascript·vue.js
LRH6 分钟前
JS基础 - instanceof 理解及手写
前端·javascript
小小神仙9 分钟前
JSCommon系列 - 为什么前端没有 Apache Commons?
前端·javascript·设计模式
WildBlue10 分钟前
🚀 React组件化实战:用TodoList项目搭乐高式开发!🎉
前端·react.js
一头小鹿10 分钟前
【JS】手写显示绑定改变this指向的方法call、apply、bind | 笔记整理
javascript
Sun_light10 分钟前
深入理解 JavaScript 对象:从入门到精通
前端·javascript
中微子11 分钟前
从零构建电影展示页面:原生js Web开发技术解析
前端·javascript
Mintopia16 分钟前
计算机图形学中的几何体布尔运算:一场形状的奇幻冒险
前端·javascript·计算机图形学