在NodeJS中使用绝对路径导入ES6模块,无需使用Babel/Webpack

19

我有一个普通的NodeJS项目(使用Typescript),我很难找到如何在本地文件中进行ES6导入而不必一直使用"../../../foo/bar"。

有许多类似的问题,但似乎都与我没有使用的babel/Webpack有关。

如果我执行以下操作:

import foo from `/bar`

它在我的电脑的根文件夹中寻找它 (例如c:/bar) 并失败了。

我已经尝试使用一个 .env 文件,其中 NODE_PATH 设置为各种不同的东西 ("/", "." 等),但没有成功。我还尝试在我的 package.json 中设置 "type: 'module'",以及我的 tsconfig.json 文件有 {"baseUrl": "."}。

所以我觉得我已经尝试了我能找到的所有答案。我是只是组合方式不对还是解决方案有所不同?


我经常使用 "../../../foo/bar",VScode 帮助我完成了这个过程。它在我输入时显示路径建议,我做这个没有任何问题。 - Jeremy Thille
NODE_PATH在ES6模块中无效。 - zloctb
4个回答

9

以下是我在这方面使用 Node.js 和本地 ES 模块的两个技巧。

  1. file: 依赖项

如果你想要从一个下面两级的子包中访问 <project root>/bar,可以将下述代码添加到 package.json 的依赖项中:

    "@local/bar": "file:../../bar",

..使得bar可以通过@local/bar在所述的子包中使用。

虽然相对路径仍然存在,但它们现在都在package.json文件中,并且源代码无需知道它们的存在。

  1. 使用动态import()

将根文件夹路径选为常量并执行此操作:

const foo = await import(`${rootPath}/bar`);

动态导入会导致 IDE 失去一些代码智能吗? - Jcyrss
1
@Jcyrss 可能会有用。我现在已经成功使用了替代方案1一段时间了。它需要npm >= 7.7,但如果可以的话,我建议使用它。这将保留动态导入仅用于确实需要它的情况。 - akauppi

6

使用“Imports”属性

截至2023年3月,消除NodeJS相对路径的好方法是在package.json中使用imports属性。有关更多信息,请参阅此帖子:

在下面的代码中,#root是项目根目录。

(如果它们对您有所帮助,请友善地为此答案和此帖子点赞。谢谢!)

对于CommonJS风格的JavaScript:

// package.json
{
  "imports": {
    "#root/*.js": "./*.js"
  }
}

// main.js:
const Source = require('#root/path/to/Source.js');

// Source.js:
module.exports = class Source {
  // ...
}

对于 ECMAScript 风格的 JavaScript:

// package.json:
{
  "type" : "module",
  "imports": {
    "#root/*.js": "./*.js"
  }
}

// main.js
import { Source } from '#root/path/to/Source.js';

// Source.js:
export class Source {
  // ...
}

优点:

  • 无需“导入”或“引用”任何其他包(无需Babel.js、Webpack或RequireJS)。安装NodeJS后,此方法即可开箱即用。

  • IDE链接按预期工作(Ctrl + 单击类名可直接跳转到源文件。同时,移动源文件(通过拖放)将自动更新文件路径引用。已在WebStorm 2022.3.2和VS Code 1.76.2上进行测试。)

  • 适用于.mjs(ECMAScript模块系统)和.cjs(CommonJS)文件类型。请参见此有关.cjs和.mjs的参考文章。

  • 无需修改保留的node_modules目录

  • 无需在操作系统级别设置任何Linux文件链接


1
应该明确指出这里需要使用井号(#),根据规范,如果没有井号,它将无法正常工作。 - undefined

1

对我来说,使用VSCode中的Typescript实现这很简单。

tsconfig.json中,在compilerOptions下添加"baseUrl": "./",

重新启动VSCode后,VSCode将自动使用伪绝对路径进行导入。

路径不会以/开头,仍然指向驱动器根目录。

如果文件位于当前文件下方,则仍将使用./relative/path,但不再使用../../tree/traversing

然后我将packages.json中的dev设置为

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "dev": "cross-env NODE_PATH=./ nodemon ./app.ts"
},

我使用 cross-env。你可以使用 set 命令和 nodemon 来自动重新加载更改的文件。

NODE_PATH 设置为 ./ 可以告诉 NodeJS 执行 Visual Studio 和 TypeScript 所做的操作。

我的 src 目录中有 package.json 文件。你可能没有,需要更改一些路径来进行调整。


-1

我不确定你所说的“本地文件而无需执行"../../../foo/bar"”是什么意思。

让我解释一下Javascript如何处理导入。这是一个基本的文件夹结构。

C:Users/NAME/Project/randomfolder/bar.js


- Project
  - Random Folder
    - foo.js
  - bar.js

选项1 对于您将要处理的95%情况可能是更好的选项

假设你正在尝试将foo.js导入到bar.js中,我们首先使用.获取bar.js的当前位置,因此它应该是:

import foo from "./Random Folder/foo.js"

如果你要往另一个方向走,. 仍然用于表示获取当前文件夹结构中的位置,但是你需要传递第二个 .,然后加上 / 来返回上一级文件夹。因此,要将 bar.js 导入到 foo.js 中,代码如下:
import bar from "../bar.js"

我们要向上一级文件夹,然后查找我们想要的文件。

选项2

但是,如果您知道您的文件夹结构将非常大,并且您将始终导入几个文件夹,为什么不创建一个变量,然后使用字符串字面值呢?

  let folder = `../../../`
  import test from `${folder}foo`

你有几个选项来处理你想要做的事情。

选项3 适用于NodeJS和模块

如果你正在使用NodeJS并且想要获取非相对路径,则可以使用app-root-path模块。它允许你始终获取项目根目录,然后相应地挖掘文件。这可以通过字符串字面量实现。

var appRoot = require('app-root-path');
import foo from appRoot + "/foo/bar/folders/bla/bla"

or

import foo from `${appRoot}/foo/bar/folders/bla/bla` <-- string literals


1
不幸的是,这两个选项仍然使用相对URL,这意味着如果我将文件移动到另一个文件夹,我必须更新每个导入。我需要的是能够使用我的项目根目录作为起点(而不是硬盘的根目录),例如从'/controllers/bar'导入foo,其中控制器文件夹位于我的项目的顶层。这样,无论我在文件夹结构中放置'bar'文件,它都可以正常工作。 - jonhobbs
由于您正在使用NodeJS,因此可以使用一个名为app-root-path的包:https://www.npmjs.com/package/app-root-path。然后,您只需要声明一个变量来存储appRoot路径并将其添加到那里即可。我会更新我的答案。 - Zodsmar
1
你如何使模板字面量与 import 一起工作?当我尝试在 Node 14.16.1 中使用此语法时,会出现 SyntaxError: Unexpected template string。插值可以与顶级 await + 动态导入语法一起使用,但我发现你的方法更易读。 - geoffrey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接