Node.js 全球化 es6 模块以像 ImportScripts 一样工作

Posted

技术标签:

【中文标题】Node.js 全球化 es6 模块以像 ImportScripts 一样工作【英文标题】:Node.js Globalize es6 modules to act like ImportScripts 【发布时间】:2022-01-20 13:17:41 【问题描述】:

问题很简单,我们如何让 es6 模块表现得像网页浏览器上使用的 ImportScript 函数。

说明

主要原因是为了减轻对开发人员的打击,因为他们将代码从 es5 语法更改为 es6,以便在您进行更改并发现由于缺少一千个错误时,转换不会炸毁您的代码夹杂物。如果您根本不想进行全部更改,它还可以让人们选择无限期地保持原样。

期望的输出

ImportScript(一个文件路径/'s);可以在随后需要的代码和主文件内的虎钳中全局(隐式)应用,以避免显式包含在所有文件中。

ES6 包含

这仍然没有忽略您的所有库也将依赖于模块格式的事实。所以不可避免的是,我们仍然必须在我们需要的每个文件中包含导出语句。但是,这不应限制我们拥有一个将它们全部互连的主文件,而无需在需要特定功能时显式添加到每个文件中。

免责声明

(编号):

    (安全)我知道模块存在的原因有很多,出于安全原因/加载时间,不建议绕过它们。但是,如果您只在应用程序生命周期开始时执行一次并且使用不接受的恒定值,即使使用“eval()”之类的方法来包含此类脚本,我也不确定风险(如果有的话)外部输入。该理论是,如果外部实体能够在启动时更改程序的初始状态,那么您的系统已经受到损害。因此,我认为围绕全球化与模块的整个争论归结为正在完成的项目(需要安全/速度)和偏好/风险。

    (不适合所有人)这是一个实用程序,我并不是说每个人都使用它

    (已发表作品)我已经为此功能进行了很多搜索,但我并非绝对会犯错误。因此,如果已经按照本规范(或更简单)对此进行了简单的使用,我很想知道如何/在哪里可以获得这样的代码。然后我会立即将其标记为答案或完全删除此线程

示例代码

ES5方式

const fs = require('fs'); 
let path = require('path'); 
    /* only accepts the scripts with global variables and functions and 
       does not work with classes unless declared as a var.
    */ 

    function include(f)  
        eval.apply(global, [fs.readFileSync(f).toString()]) 
    

主文件概念示例:

        ImportScript("filePath1");loaded first 
        ImportScript("filePath2");loaded second
        ImportScript("filePath3");loaded third
        ImportScript("filePath4");loaded fourth
        ImportScript("filePath5");loaded fifth
       ImportScript("someExternalDependency");sixth  
    
        /* where "functionNameFromFile4" is a function defined in 
         file4 , and "variableFromFile2" is a global dynamic 
         variable that may change over the lifetime of the 
         application.
        */

        functionNameFromFile4(variableFromFile2); 

        /* current context has access to previous scripts contexts 
         and those scripts recognize the current global context as 
         well in short: All scripts should be able to access 
         variables and functions from other scripts implicitly 
         through this , even if they are added after the fact
        */ 

典型的导出文件示例(涵盖通过模块导出的所有方法):

/*where "varFromFile1" is a dynamic variable created in file1
that may change over the lifetime of the application and "var" is a 
variable of type(varFromFile4) being concatenated/added together 
with "varFromFile4".
*/

functionNameFromFile4(var)
    return var+varFromFile1;


//Typical export statement
exportAllHere;

/* 
This is just an example and does not cover all usage cases , just 
an example of the possible functionality
*/

结论

因此,您仍然需要按照 es6 标准的要求导出文件,但您只需在主文件中导入一次,即可在所有脚本中全球化其功能。

【问题讨论】:

你没有。 ES6 模块是不同的。它们有不同的规则,它们有不同的特性,等等……它们不一样,不能和 CommonJS 模块一样。您的“结论”中的那一行提出了一些完全不同的东西,它与您想要导入一次并在全球范围内可用的模块化100%相反。这与require()import 完全没有关系,所以用那个结尾句,你完全混淆了你的整个问题。 好吧,我可以重新整理我的问题,让它更清楚一点。 仅供参考,模块化的反面是将很多东西分配给全局范围。您可以通过将属性显式分配给global 对象来做到这一点,但不建议这样做,因为 nodejs 的核心是模块化系统,同样的原因是 javascript 语言本身正在迁移到模块化系统。许多全球性的事情造成了开发人员的混乱和测试的混乱。 如果您想询问从 CommonJS 模块到 ESM 模块的过渡策略或共存策略(第 3 点似乎是关于什么),请询问并描述一个非常具体的问题需要帮助。你的问题似乎描述了非常不同的事情。实际上,您似乎在问题中混淆了一大堆不同的东西,并以某种方式将这一切归咎于require()import,而其中大部分与此无关。 没有多少免责声明可以将一个不明确的问题变成一个明确的问题。如果您想在这里获得帮助的最佳机会,您需要写一个明确的问题。我已经提出了我的想法(在 cmets 中),让模块在全球范围内可用,而无需在每个你想使用它们的地方导入或要求它们。 【参考方案1】:

我个人并不喜欢将一个模块的所有导出都全球化,但这里有一个小 sn-p,它向您展示了如何将一个 ESM 模块的导出全部分配给全局对象:

假设您有一个名为 operators.js 的简单模块:

export function add(a, b) 
    return a + b;


export function subtract(a, b) 
    return a - b;

您可以导入该模块,然后将其所有导出的属性分配给全局对象:

import * as m from "./operators.js"

for (const [prop, value] of Object.entries(m)) 
    global[prop] = value;


// can now access the exports globally
add(1, 2);

仅供参考,我认为语法:

include("filePath1")

在 ESM 模块中会很困难,因为使用 import 的 ESM 模块中的动态导入(这可能是您实现 include() 所显示的函数所必须使用的)是异步的(它们返回一个承诺) , 不像 require() 那样同步。


我想知道是否可以选择打包器或转译器?

这里有与自定义加载器相关的nodejs实验工作:https://nodejs.org/api/esm.html#hooks。


如果您可以处理您的 include() 函数返回一个承诺,那么您可以将上述代码放入该函数中:

async function include(moduleName) 
    const m = await import(moduleName);
    for (const [prop, value] of Object.entries(m)) 
        global[prop] = value;
    
    return m;

【讨论】:

如果它易于实现并且涵盖容易全球化的事物,那么它对我有用。如果您需要做的就是包含一个可以调用它的库或类似的东西。这样做的目的是让程序员能够编写代码,以便稍后(一旦生成)更改为 ES6 或根据情况/愿望保持。它是为了减少编码的痛苦,因为有时您只是想删除一个函数,而不会破坏导出/导入语句,同时易于实现。我想我看到了一个 exportAll 声明。 @static660 - 我不知道您是否看到了我最近添加的其余答案。您不能将我在第二个代码块中的代码放入一个函数中然后调用它,因为您不能在函数内执行这种类型的import。它只能在模块的顶层完成。在函数内执行import 必须作为函数调用import("module") 完成,并成为dynamic 导入,它不是同步的并返回一个promise。 没关系,我总是可以将它包装在异步包装器中,然后在加载承诺后初始化程序。 @static660 - 好的,我将该代码放入一个返回承诺的include() 函数中(添加到我的答案中)。您必须管理如何“等待”承诺解决。

以上是关于Node.js 全球化 es6 模块以像 ImportScripts 一样工作的主要内容,如果未能解决你的问题,请参考以下文章

Node.js 的 commonJS 规范 ES6 导入 js 文件

Node.js 的 commonJS 规范 ES6 导入 js 文件

es6和node.js模块的区别

使用Node.js需要与ES6导入/导出

使用 ES6 模块时 Node.js 中 __dirname 的替代方案

node.js的模块化与包