使用 npm 7 工作区简化你的 monorepo

2025-06-07

使用 npm 7 工作区简化你的 monorepo

UI React 组件的热重加载

本月,npm发布了其包管理器npm 7的主要版本。它附带了对工作区的支持。

为什么这是个大新闻?因为 npm 是唯一一个与每个 NodeJS 捆绑在一起的包管理器。要使用yarnpnpm,你必须先额外安装它们。

继续阅读,您将了解如何在实际场景中使用 npm 7 工作区,并了解 npm 使用工作区的方式与 yarn 使用工作区的方式非常不同。

Monorepo 用例

Monorepo 是一个描述包含许多项目的单个 git 存储库的术语。

设置 monorepo 的最常见原因是为了简化维护使用共享代码(例如通用用户界面库)的多个应用程序的开发团队的工作。

想象一下,一个团队开发了两个 React 应用程序,它们共享一些常见的 UI 元素,如输入、选择器、手风琴等。最好以 React 组件的形式提取该 UI,并准备好可供团队所有成员使用的构建块。

除此之外,在单个 IDE 实例中打开所有源文件也更加方便。您无需在桌面上切换窗口,即可在各个项目之间跳转。

我也希望我的应用程序中有那个漂亮的按钮

假设我想构建两个独立的 React 应用,分别名为app1和 ,app2它们将使用来自通用 UI 库 的通用组件ui。我希望每当我编辑 UI 库中的文件时,这两个应用都能热重载。

我所说的独立是指app1对某事一无所知app2,反之亦然。

以下是与 npm 7 工作区兼容的设置。

Monorepo 结构

在 npm 7 中定义工作区

我给所有包名都加上了@xyz前缀,这样它们就能与官方 npm 仓库中的包名区分开来。只需将其改为你自己的即可。

这是整个设置中最关键的部分。将以下内容插入到您的根文件夹中,package.json以设置 monorepo。

{
    "name": "@xyz/monorepo",
    "private": true,
    "version": "1.0.0",
    "workspaces": [
        "./common/*"
    ]
}
Enter fullscreen mode Exit fullscreen mode

新的"workspaces"属性让 npm 知道我想要跟踪文件夹内的任何包,并在运行时./common自动在根目录中对它们进行符号链接node_modulesnpm 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"
}
Enter fullscreen mode Exit fullscreen mode

以及index.js将导出字符串的文件:

const version = "This comes from UI! 1.0.0"

export default version;
Enter fullscreen mode Exit fullscreen mode

是时候将该字符串导入我们的应用程序了。

在我们的 monorepo 中快速创建 React 应用。只需使用新发布的 Create React App 4:

mkdir apps
cd apps
npx create-react-app app1
npx create-react-app app2
Enter fullscreen mode Exit fullscreen mode

现在你可能认为我们需要将ui库添加到应用中。在 Yarn 中,它看起来如下所示:

yarn workspace app1 add @xyz/ui
Enter fullscreen mode Exit fullscreen mode

但是有了 npm 我们根本不需要添加任何依赖。

只需转到app1app2App.js应用程序中的文件并添加以下代码即可显示来自我们的 UI 库的字符串:

...
import testString from "@xyz/ui"; 
...
    <span>{testString}</span>
...
Enter fullscreen mode Exit fullscreen mode

要测试它,请使用以下命令:

# 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
Enter fullscreen mode Exit fullscreen mode

并从另一个终端窗口:

cd apps/app2
npm install
npm run start
Enter fullscreen mode Exit fullscreen mode

您将看到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
}
Enter fullscreen mode Exit fullscreen mode

common/ui/babel.config.js

module.exports = {
  presets: [
    [
      "@babel/preset-react",
      {
        targets: {
          node: "current",
        },
      },
    ],
  ],
};
Enter fullscreen mode Exit fullscreen mode

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'
  },
};
Enter fullscreen mode Exit fullscreen mode

我们的简单组件:

common/ui/src/index.js

import React from "react";

const UIExample = ({ text = "" }) => {
  return (
    <div>
      <h1>Shared UI library {text}</h1>
    </div>
  );
};

export default UIExample;
Enter fullscreen mode Exit fullscreen mode

使用下面的方法将UIExample组件导入到您的 React 应用程序中:

apps/app1/src/App.js

...
import UIExample from "@xyz/ui";
...
    <div>
        <UIExample text="from app1" />
    </div>
...
Enter fullscreen mode Exit fullscreen mode

确保每次代码更改时都转换 UI 库:

cd common/ui
npm run build-watch
Enter fullscreen mode Exit fullscreen mode

在单独的终端窗口中运行 app1,并注意,每当您编辑 UI 组件时,由于在后台运行的 webpack 监视,webpack dev 服务器都会自动使用最新版本重新加载它。

cd apps/app1
npm run start
Enter fullscreen mode Exit fullscreen mode

演示

下面我正在编辑通用 UI 组件UIElement,保存后,两个 React 应用程序都会自动刷新更新后的组件:

热重载

概括

借助最新的 npm 7 及其对工作区的支持,现在可以拥有 monorepo,而无需任何外部工具(如@react-workspacesnx)。

请记住,npm 的理念与 yarn 不同。例如,您无法从 monorepo 的根文件夹运行工作区内的脚本。

另请注意,monorepo 的 package.json 属性中未定义@xyz/app1。只有需要导出的模块才需要在此处定义 ( )。@xyz/app2workspaces@xyz/ui

npm 7 的工作区主要提供模块的发现功能。我希望发行说明中能强调这一点,并且 npm 的帮助示例能更复杂一些。我希望这篇文章能暂时填补这个空白。

奖金

查看我的gif-css-animation-monorepo存储库,其中展示了我如何使用HTML 页面为本文制作动画

文章来源:https://dev.to/limal/simplify-your-monorepo-with-npm-7-workspaces-5gmj
PREV
2025 年学习 Golang,你不会后悔
NEXT
Web 开发工具和资源