使用 npm 7 工作区简化你的 monorepo
本月,npm发布了其包管理器npm 7的主要版本。它附带了对工作区的支持。
为什么这是个大新闻?因为 npm 是唯一一个与每个 NodeJS 捆绑在一起的包管理器。要使用yarn或pnpm,你必须先额外安装它们。
继续阅读,您将了解如何在实际场景中使用 npm 7 工作区,并了解 npm 使用工作区的方式与 yarn 使用工作区的方式非常不同。
Monorepo 用例
Monorepo 是一个描述包含许多项目的单个 git 存储库的术语。
设置 monorepo 的最常见原因是为了简化维护使用共享代码(例如通用用户界面库)的多个应用程序的开发团队的工作。
想象一下,一个团队开发了两个 React 应用程序,它们共享一些常见的 UI 元素,如输入、选择器、手风琴等。最好以 React 组件的形式提取该 UI,并准备好可供团队所有成员使用的构建块。
除此之外,在单个 IDE 实例中打开所有源文件也更加方便。您无需在桌面上切换窗口,即可在各个项目之间跳转。
我也希望我的应用程序中有那个漂亮的按钮
假设我想构建两个独立的 React 应用,分别名为app1
和 ,app2
它们将使用来自通用 UI 库 的通用组件ui
。我希望每当我编辑 UI 库中的文件时,这两个应用都能热重载。
我所说的独立是指app1
对某事一无所知app2
,反之亦然。
以下是与 npm 7 工作区兼容的设置。
在 npm 7 中定义工作区
我给所有包名都加上了@xyz前缀,这样它们就能与官方 npm 仓库中的包名区分开来。只需将其改为你自己的即可。
这是整个设置中最关键的部分。将以下内容插入到您的根文件夹中,package.json
以设置 monorepo。
{
"name": "@xyz/monorepo",
"private": true,
"version": "1.0.0",
"workspaces": [
"./common/*"
]
}
新的"workspaces"
属性让 npm 知道我想要跟踪文件夹内的任何包,并在运行时./common
自动在根目录中对它们进行符号链接。node_modules
npm install
从现在起,每当我们的 React 应用使用import Foo from "@xyz/ui"
NodeJS 时,它都会在./node_modules/common/@xyz/ui
指向包含我们库的文件夹中找到它。完美!有了工作区,就再也./common/ui
不需要了。npm link
如果没有工作区,React 应用程序会抱怨它找不到名为的模块@xyz/ui
,并会开始在 npm 官方注册表中寻找它。
迈出第一步
为了测试我们的设置,让我们从ui
库中共享一个文本并将该字符串导入我们的 React 应用程序。
创建通用 UI 库package.json
:
{
"name": "@xyz/ui",
"version": "1.0.0",
"private": true,
"main": "index.js"
}
以及index.js
将导出字符串的文件:
const version = "This comes from UI! 1.0.0"
export default version;
是时候将该字符串导入我们的应用程序了。
在我们的 monorepo 中快速创建 React 应用。只需使用新发布的 Create React App 4:
mkdir apps
cd apps
npx create-react-app app1
npx create-react-app app2
现在你可能认为我们需要将ui
库添加到应用中。在 Yarn 中,它看起来如下所示:
yarn workspace app1 add @xyz/ui
但是有了 npm 我们根本不需要添加任何依赖。
只需转到app1和app2App.js
应用程序中的文件并添加以下代码即可显示来自我们的 UI 库的字符串:
...
import testString from "@xyz/ui";
...
<span>{testString}</span>
...
要测试它,请使用以下命令:
# create a symlink to the @xyz/ui in the root folder
npm install
# go to the app's folder
cd apps/app1
# For CRA 4 you may need to add SKIP_PREFLIGHT_CHECK=true to .env file
# And use the --legacy-peer-deps flag as many packages hasn't been updated yet to officially support React 17
npm install --legacy-peer-deps
npm run start
并从另一个终端窗口:
cd apps/app2
npm install
npm run start
您将看到This comes from UI! 1.0.0
两个 React 应用程序中呈现的文本!
导出 React JSX 组件
现在,如果你尝试导出一个 JSX 组件,React 应用会报错说无法解析该 JSX。你需要先从通用 UI 包中转译 JSX 代码。
您可以使用基本的 Webpack 5 设置:
common/ui/package.json
{
"name": "@xyz/ui",
"version": "0.2.0",
"private": true,
"module": "build/ui.bundle.min.js", # Changed main to module
"scripts": {
"build": "webpack --config webpack.prod.js",
"build-watch": "webpack --config webpack.prod.js --watch",
},
... # webpack 5 dependencies
}
common/ui/babel.config.js
module.exports = {
presets: [
[
"@babel/preset-react",
{
targets: {
node: "current",
},
},
],
],
};
common/ui/webpack.prod.js
const path = require("path");
module.exports = {
entry: {
index: { import: "./src/index.js" }
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: "babel-loader",
},
],
},
output: {
filename: "ui.bundle.min.js",
path: path.resolve(__dirname, "build"),
// Below two important lines!
library: 'xyzUI',
libraryTarget: 'umd'
},
};
我们的简单组件:
common/ui/src/index.js
import React from "react";
const UIExample = ({ text = "" }) => {
return (
<div>
<h1>Shared UI library {text}</h1>
</div>
);
};
export default UIExample;
使用下面的方法将UIExample
组件导入到您的 React 应用程序中:
apps/app1/src/App.js
...
import UIExample from "@xyz/ui";
...
<div>
<UIExample text="from app1" />
</div>
...
确保每次代码更改时都转换 UI 库:
cd common/ui
npm run build-watch
在单独的终端窗口中运行 app1,并注意,每当您编辑 UI 组件时,由于在后台运行的 webpack 监视,webpack dev 服务器都会自动使用最新版本重新加载它。
cd apps/app1
npm run start
演示
下面我正在编辑通用 UI 组件UIElement
,保存后,两个 React 应用程序都会自动刷新更新后的组件:
概括
借助最新的 npm 7 及其对工作区的支持,现在可以拥有 monorepo,而无需任何外部工具(如@react-workspaces
或nx
)。
请记住,npm 的理念与 yarn 不同。例如,您无法从 monorepo 的根文件夹运行工作区内的脚本。
另请注意,monorepo 的 package.json 属性中未定义@xyz/app1
和。只有需要导出的模块才需要在此处定义 ( )。@xyz/app2
workspaces
@xyz/ui
npm 7 的工作区主要提供模块的发现功能。我希望发行说明中能强调这一点,并且 npm 的帮助示例能更复杂一些。我希望这篇文章能暂时填补这个空白。
奖金
查看我的gif-css-animation-monorepo存储库,其中展示了我如何使用HTML 页面为本文制作动画。
文章来源:https://dev.to/limal/simplify-your-monorepo-with-npm-7-workspaces-5gmj