【问题标题】:Define global variable before importing ES6 module在导入 ES6 模块之前定义全局变量
【发布时间】:2021-12-27 02:39:00
【问题描述】:

我最近在我的 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(),因为这只会让您获得 cwd。相反,请参阅 here 了解如何执行此操作。
  • 感谢您的链接,我将更改__dirname。关于进出口,我只是不明白你的意思。你能更明确地回答这个问题吗?也许用一段代码?
  • 你试过globalThis

标签: javascript node.js ecmascript-6 es6-modules


【解决方案1】:

我喜欢 Bergi 的回答:

import appRoot from './globals';

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

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

而且,我建议您使用import.meta.url 变通方法来创建等效于__dirname 的轮廓here。您对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; 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 };

【讨论】:

    【解决方案2】:

    在我的代码中是否先调用了导入模块?

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

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

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

    然后 setupGlobals 模块代码在 my-object-file 之前执行。所以它会在做时起作用

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

    我绝对希望能够将路径定义为可在我的模块中访问的全局变量

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

    如果您有一个单独的模块来定义您的全局变量,只需将这些变量设置为 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
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-06
      • 2018-11-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多