在导入 ES6 模块之前定义全局变量

Define global variable before importing ES6 module

我最近在我的 NodeJS 项目中从 CommonJS 切换到 ES6 模块。我面临的挑战之一是在我导入我的模块之一之前定义一个全局变量。我曾经在我的主文件中使用 CommonJS 来做到这一点:


const path = require('path');

global.appRoot = path.resolve(__dirname);

const myObj = require('./my-object-file');

我的 my-object-file 使用 global.appRoot.

使用 ES6,我尝试了以下方法:

import path from 'path';

global.appRoot = path.resolve(path.resolve());

import myObj from './my-object-file';

其中 my-object-file.js 为:

export default {
    root: global.appRoot
}

但是我在 my-object-file.js 中得到 global.appRoot 的未定义。

这是怎么回事?

导入模块是否在我的代码中的任何内容之前被调用?

我该如何解决这个问题(知道我绝对希望能够将路径定义为我的模块中可访问的全局变量)?

Are import modules called before anything in my code?

是的,在导入模块中的任何代码运行之前解析所有模块导入。

然而,导入的模块也是按顺序执行的,所以如果你这样做

import './setupGlobals';
import myObj from './my-object-file';

然后 setupGlobals 模块代码在 my-object-file 之前执行。所以它会在 do

时起作用
// setupGlobals.mjs
import path from 'path';    
global.appRoot = path.resolve(path.resolve());

I absolutely want to be able to define the path as a global variable accessible in my modules

不,你真的不想那样做。显式声明您的依赖项,而不是可能在任何地方创建的全局变量!

如果你有一个单独的模块来定义你的全局变量,那么只需要 export 这些变量而不是将值放在 global 对象上:

// globals.mjs
import path from 'path';    
const appRoot = path.resolve(path.resolve());
export { appRoot as default }

然后您可以在您的任何模块中以声明方式使用此全局常量:

// my-object-file.js:

import appRoot from './globals';

export default {
    root: appRoot
}

我喜欢 Bergi 的回答:

import appRoot from './globals';

然后,任何想要访问的文件都可以访问 appRoot,并且您保留了模块化。

但是,除了这种方法之外,我在评论中的建议是,与其在导入之前设置全局变量,不如从模块中导出一个函数并调用该函数,将其传递给所需的路径。这是在不使用全局变量的情况下将初始化参数从父模块传递给模块的通用方法。

而且,我建议您使用 import.meta.url 解决方法来创建相当于 __dirname 的大纲 。您对 path.resolve() 所做的只是让您获得当前工作目录,这不一定是 __dirname,因为这取决于该模块是如何加载的,它们是否相同。此外,如果你只是想要 cwd 的等价物,你可以在你的子模块中使用 process.cwd() 。这是 ESM 模块中 __filename__dirname 的等价物。

// create __dirname and __filename equivalents in ESM module
import { fileURLToPath } from 'url';
import { dirname } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

然后,导入模块初始化函数(有时称为模块构造函数)并调用模块构造函数:

从“./my-object-file”导入 myObjConstructor; 常量 myObj = myObjConstructor(__dirname);

然后,在 my-object-file 中,导出一个函数,当调用该函数时,使用传入的 __dirname 和 return 初始化模块 myObj.

因此,my-object-file 内部:

function init(__dirname) {
   // do whatever you want for module initialization
   // using __dirname

   return yourObj;
}

export { init as default };