从 npm 包的子文件夹导入

Import from subfolder of npm package

我一直致力于创建一个小型的 React 组件库,用于其他几个项目。我在内部发布包(使用私有 GitHub 存储库),然后包含在另一个项目中。但是,当我从包的子目录导入时,我无法这样做,因为路径不匹配。

使用该包的项目都使用 webpack 来 bundle/transpile 代码,因为我试图避免在组件库中执行 any building 如果可能。

目录结构

- package.json
- src/
  - index.js
  - Button/
    - index.js
    - Button.jsx
    - ButtonGroup.jsx
  - Header/
    - index.js
    - Header.jsx (default export)

package.json

...
"main": "./src/index.js",
"scripts": "",
...

src/Button/index.js

import Button from './Button';
import ButtonGroup from './ButtonGroup';

export default Button;

export { Button, ButtonGroup};

src/index.js

如果仅从子目录导入,是否真的需要此文件?

import Button from './Button';
import ButtonGroup from './Button/ButtonGroup';
import Header from './Header';

export { Button, ButtonGroup, Header };

其他项目

// This project is responsible for building/transpiling after importing
import { Button, ButtonGroup } from 'components-library/Button';

例子

Material-UI 是 Requiring 以下列方式使用的 React 组件库:import { RadioButtonGroup } from 'material-ui/RadioButton。我试图弄清楚这对他们有何作用,但无济于事。

类似问题

问题

  1. 我可以在导入路径中以某种方式跳过 src/ 目录吗?
  2. 我可以跳过包中任何类型的 build 阶段吗(这样开发人员在提交之前不必 build)?
  3. 类似material-ui的包如何处理这个问题?

答案可能取决于您安装组件库的方式。如果您通过 npm install <git-host>:<git-user>/<repo-name>npm install <git repo url>

完成
  1. 你应该可以 import {Button} from 'component-library/Button' 照原样,根据 . Similar to Node's require() resolution, Webpack should resolve subdirectories within component-library relative to component-library's entry point. You can find the docs on customizing the resolution behavior via the webpack.config.resolve property. material-ui 似乎依赖于从模块入口目录解析子目录导入。

  2. 分发ES模块库,分发前无需构建。但是,create-react-app 等项目可能需要 pre-transpiled 版本。

或者,您可以写 import {Button} from 'components-library'。 Webpack 将毫不费力地通过每个 index 追溯依赖关系。

其中一个可能的解决方案是 webpack 别名系统。
您可以创建另一个项目,例如将其命名为 'app-aliases',这样您的别名就可以重复使用了。
该项目将有一个包含所有包路径的 js 文件:

const path = require('path');

module.exports = {
'@components': path.resolve(__dirname, 'node_modules/components-library/src'),
'@another': path.resolve(__dirname, 'node_modules/any/path/you/want'),
}

然后将其添加到负责building/transpiling的任何项目的webpack配置中:
webpack.config.js

const appAliases = require('app-aliases');

const config = {
...
  resolve: {
    alias: {
      ...appAlises
    }
  }
}

在运行时代码中,您将能够像这样使用它:

import {Button} from '@components/Button';
import {Something} from '@another'

如果您使用的是打字稿,则需要将相同的别名添加到 paths tsconfig 属性.
所以你的问题的答案是:

  1. 是的,您可以在别名中使用任何路径
  2. 是的,没有必要构建所有项目
  3. 我看到现在 mui 使用 directi 包的导入(例如核心),参见 https://material-ui.com/components/radio-buttons/import Radio from '@material-ui/core/Radio';。但我希望他们使用我在下面描述的再出口。

还有关于node.js解析机制。
当您导入某个库时,它会尝试在其中找到 node_modules/some-library/package.json 然后 main 属性。这个 属性 应该会导致您的主要入口点。通常它是 src/index.js (如果还没有,你应该在 package.json 中设置它)。在此文件中,您可以从内部文件结构中重新导出任何您想要的内容,并且无需完整路径即可使用它。 请参阅此 repo 以获取一些示例。

我是一名 angular 开发人员,从未使用过 React,但我可以说 material-ui 正在使用 monorepo,其中我们在 angular 中存在相同的概念创建一个工作区,该工作区包含多个 project/packages,如 react 中所命名。了解更多信息 Workspaces with Yarn

Material-ui 在 tsconfig 中使用虚假路径使其看起来像 src 文件夹不存在这来自您提供的 git:tsconfig.json

我可以在导入路径中以某种方式跳过 src/ 目录吗?

是的。使用 package.json "exports" 字段,Webpack 应该会在不久的将来支持它(参见 issue), but has already been supported by Node since Node 12 LTS following the Bare Module Specifier Resolution proposal:

package.json

...
"main": "./src/index.js",
"type": "module",
...
"exports": {
  "./Button": "./src/Button/index.js",
  "./Header": "./src/Header/index.js"
},
...

现在,代码如下:

// This project is responsible for building/transpiling after importing
import { Button, ButtonGroup } from 'components-library/Button';

应翻译为:

import { Button, ButtonGroup } from 'components-library/src/Button/index.js';

应该正确导入请求的模块。

警告

现在,尝试更简单的版本肯定很诱人:

...
"exports": {
  "./Button": "./src/Button/",
  "./Header": "./src/Header/"
},
...

so 作为通常的 import 语句

import { ... } from 'components-library/Button';

被翻译成

import { ... } from 'components-library/src/Button';

这看起来不错,但是 在这种情况下它不起作用,因为您的子模块没有各自的 package.json 文件,而是依赖于它们的 index.js 要查找的文件。

/!\ Unlike in CommonJS, there is no automatic searching for index.js or index.mjs or for file extensions.


src/index.js - 如果仅从子目录导入,是否真的需要此文件?

我不这么认为,但如果你愿意,可以保留它。


我可以跳过包中任何类型的构建阶段吗?

使用 "exports" 字段不需要您转换代码。

你必须安装babel-plugin-module-resolver

在你的 .babelrc 文件别名中指定包的相对路径

{
  "plugins": [
    ["module-resolver", {
      "alias": {
        "components-library": "./node_module/components-library"
      }
    }]
  ]
}

然后你可以像这样导入 npm 包的子目录

import { Button, ButtonGroup } from 'components-library/Button';

这是可能的,但需要发布一个精选的 dist 文件夹,而不是项目的根目录。

如果您了解模块解析的工作原理,整个事情就相当简单,您只需要一个小脚本来准备您的分发。

以免我在这里重复所有细节,请参阅我对 的回答。