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

5

我最近在我的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的值未定义。

这是怎么回事?

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

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


全局变量只是一种糟糕的设计模式,违反了模块化的几个原则。为什么不改变导入的模块,使其导出一个函数,在导入后调用该函数并传递要使用的目录。这将在CommonJS和ESM模块中都起作用。如果您正在尝试获取ESM模块中__dirname的等效值,则不应使用path.resolve(),因为这只会获取当前工作目录。请参见此处以了解如何执行此操作。 - jfriend00
谢谢提供链接,我会更改__dirname。关于导入和导出,我不太明白你的意思。你能否更明确地回答这个问题?可以给一个代码片段作为例子吗? - Nate
你尝试使用了 globalThis - bill.gates
2个回答

8

我的代码中的所有导入模块都在任何代码运行之前被调用吗?

是的,在导入模块的代码运行之前,所有模块导入都会被解析。

然而,导入的模块也会按顺序执行,因此如果您这样做:

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

在执行my-object-file模块代码之前,会先执行setupGlobals模块的代码,因此当执行时它会生效。

// setupGlobals.mjs
import path from 'path';    
global.appRoot = path.resolve(path.resolve());

我希望能够定义路径作为全局变量,并在我的模块中访问该变量。

不,您真的不需要那样做。与其创建可能在任何地方创建的全局变量,不如明确声明依赖关系!

如果您已经有一个单独的模块来定义全局变量,只需使该模块将这些变量导出,而不是将值放在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
}

1

我喜欢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);

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

import myObjConstructor from './my-object-file'; const myObj = myObjConstructor(__dirname);

接下来,在my-object-file中,您需要导出一个函数,当该函数被调用时,使用传入的__dirname初始化您的模块,并返回myObj

因此,在my-object-file内部:

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

   return yourObj;
}

export { init as default };

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