【问题标题】:How to share React components in monorepo, by source?如何在 monorepo 中按源共享 React 组件?
【发布时间】:2021-12-02 12:15:11
【问题描述】:

我有一个包含 3 个不同 React 项目的 monorepo:

- SHARED_COMPONENTS
  src
    Button.tsx
  package.json
- APP_A
  src
    main.ts
  package.json
- APP_B
  src
    main.ts
  package.json

如何从两个 main.ts 文件中导入 Button(.tsx) 模块?例如。我想在 SHARED_COMPONENTS 文件夹中定义我的共享组件,并在 APP_A 和 APP_B 中使用这些组件。

我不想编译 Button(并与 bit.dev 或 npm 注册表等工具共享)。我想包含 Button.tsx 的 源文件,并让每个单独的构建脚本(用于 APP_A 和 APP_B 的脚本)编译/转译 Button.tsx 的源代码。

也就是说,Button.tsx 应该成为 f.e. 源代码的一部分。 APP_A。我不想将其作为编译组件包含在内。

所有项目(SHARED_COMPONENTS、APP_A、APP_B)都是使用 CRA 创建的。但是如果有必要,可以覆盖 webpack 配置,或者我们可以弹出并手动进行。

我认为主要问题是 ../SHARED_COMPONENTS 位于每个单独的 APP 项目的 src 目录之外,因此我们不能包含来自共享组件的源代码。

有解决方案吗?非常感谢您提供指向指南或详尽解释的链接。我尝试过广泛搜索,但我发现的材料通常建议打包共享组件并与 bit.dev/npm 注册表共享它们,这是我们不想要的。我们目前正在尝试这个,这对于开发周期来说非常繁琐。

【问题讨论】:

  • 我很想知道这方面的最新想法。

标签: javascript reactjs webpack


【解决方案1】:

我过去曾使用 craco 解决过此类问题。 一般的想法是让 craco 修改 webpackConfig 以在 babel-loader 的设置中包含 SHARED_COMPONENTS 的目录。您还需要为您的 SHARED_COMPONENTS 添加一个别名,以便您可以轻松导入它们。下面是它的样子:

按照 README 中的说明安装 craco: https://www.npmjs.com/package/@craco/craco#installation

然后安装 craco-babel-loader。尽管有 CRA 的默认规则,这将帮助您在 /src 目录之外添加文件以进行编译。 npm install craco-babel-loader

然后转到您的 craco.config.js 文件并进行以下更改

// APP_A/craco.config.js

const path = require("path");
const fs = require("fs");
const cracoBabelLoader = require("craco-babel-loader");

/**
 * CRA has a ModuleScopePlugin that prohibits imports outside of the /src
 * directory. If you have code outside this directory (e.g. components shared
 * between multiple projects) then this becomes an issue. This plugin takes
 * options: {path: "/asbolute-path", name: "alias-you-want"} NOTE: For this to
 * work, the directory that you point to must have a package.json file
 */
const enableImportOutsideSrcDir = {
  overrideWebpackConfig: function enableImportOutsideSrcDir({
    webpackConfig,
    pluginOptions,
    context: { paths, name },
  }) {
    const absolutePath = path.join(paths.appPath, pluginOptions.path);
    const moduleScopePlugin = webpackConfig.resolve.plugins.find(
      (plugin) => plugin.appSrcs && plugin.allowedFiles
    );

    if (moduleScopePlugin) {
      moduleScopePlugin.appSrcs.push(absolutePath);
    }

    webpackConfig.resolve.alias = Object.assign(webpackConfig.resolve.alias, {
      [pluginOptions.name]: absolutePath,
    });

    return webpackConfig;
  },
};

// Handle relative paths to sibling packages
const appDirectory = fs.realpathSync(process.cwd());
const resolvePackage = (relativePath) =>
  path.resolve(appDirectory, relativePath);

module.exports = {
  plugins: [
    {
      // enable compilation for the following directories outside of CRA's /src
      plugin: cracoBabelLoader,
      options: {
        includes: [resolvePackage("../SHARED_COMPONENTS")],
      },
    },
    {
      // now that we're compiling these files, enable importing them
      plugin: enableImportOutsideSrcDir,
      options: { path: "../SHARED_COMPONENTS", name: "sharedcomponents" },
    },
  ],
};

需要注意的一件有用的事情是 SHARED_COMPONENTS 必须有自己的 package.json。即使它没有依赖项并且您直接导入源文件也是如此。否则,当您尝试运行 npm run start 并抱怨 ModuleScopePlugin 抛出错误时会出错。

我必须为每个项目重复此设置。因此,您将为 APP_A 和 APP_B 安装 craco 和上述 craco.config.js。

【讨论】:

    【解决方案2】:

    我个人现在计划将我的 APP_A 和 APP_B 放在一个 react 应用程序中,并带有一个靠近根目录的开关,根据环境变量决定启用哪一个。这是在过去两年多次尝试解决同样的问题之后。

    如您所说,CRA 不能在其项目之外包含文件,而且我记得第 3 方正在开发整个框架以使 JS monorepos 工作,包括 React 代码共享,但似乎没有一个框架足够主流和成熟到让我能够承诺。

    所以我将交换捆绑包大小(对我们的小型 Web 应用程序来说不是问题),只拥有一个 CRA 应用程序。

    (我想发表评论,但我没有足够的声誉。)

    【讨论】:

    • 刚刚发现这篇博客文章描述了这种基于 env var 的应用拆分的解决方案:blog.logrocket.com/…
    • 而且代码拆分甚至可以解决包大小问题reactjs.org/docs/code-splitting.html
    • 虽然我在下面发布的解决方案有效,但在一个新项目中,这是使用 NextJS 和 Lerna 解决的。这允许导入源文件。在 Next 中,每个页面都有自己的路线,并且天生也有自己的代码拆分。因为它是一个 monorepo,所以您还可以避免在单独的 repos 中制作 PR 并保持版本同步。
    猜你喜欢
    • 2018-12-31
    • 1970-01-01
    • 1970-01-01
    • 2015-08-25
    • 2019-07-14
    • 2017-07-02
    • 2022-06-25
    • 1970-01-01
    • 2020-08-23
    相关资源
    最近更新 更多