如何在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,就可以发布啦。

结尾

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

相关推荐
Moment1 小时前
从方案到原理,带你从零到一实现一个 前端白屏 检测的 SDK ☺️☺️☺️
前端·javascript·面试
野生的程序媛2 小时前
重生之我在学Vue--第5天 Vue 3 路由管理(Vue Router)
前端·javascript·vue.js
鱼樱前端2 小时前
Vue 2 与 Vue 3 响应式原理详细对比
javascript·vue.js
codingandsleeping2 小时前
前端工程化之模块化
前端·javascript
CodeCraft Studio2 小时前
报表控件stimulsoft操作:使用 Angular 应用程序的报告查看器组件
前端·javascript·angular.js
阿丽塔~2 小时前
面试题之vue和react的异同
前端·vue.js·react.js·面试
Liigo3 小时前
初次体验Tauri和Sycamore(3)通道实现
javascript·rust·electron·tauri·channel·sycamore
烛阴3 小时前
JavaScript 性能提升秘籍:WeakMap 和 WeakSet 你用对了吗?
前端·javascript
moton20173 小时前
Flutter开发避坑指南:高频问题排查与性能调优实战
mqtt·flutter·性能优化·前端框架·自动化·dart
专注VB编程开发20年4 小时前
JS采集数据爬虫-Fetch API 和 XMLHttpRequest 有什么区别?
开发语言·javascript·爬虫·js