将选项传递给 ES6 模块导入

Posted

技术标签:

【中文标题】将选项传递给 ES6 模块导入【英文标题】:Pass options to ES6 module imports 【发布时间】:2015-07-07 13:36:31 【问题描述】:

是否可以将选项传递给 ES6 导入?

你是怎么翻译的:

var x = require('module')(someoptions);

到 ES6?

【问题讨论】:

不确定是否可以,有一个模块加载器 API,或者至少在某个时候,它使用了 System.import(module) 之类的东西,不确定是否允许参数,有人知道更多ES6 可能会? 有一个建议的解决方案,在 node.js(通过插件)和 webpack 中已经有实现:2ality.com/2017/01/import-operator.html 【参考方案1】:

单条import 语句无法做到这一点,它不允许调用。

所以你不会直接调用它,但你基本上可以像 commonjs 对默认导出所做的一样:

// module.js
export default function(options) 
    return 
        // actual module
    


// main.js
import m from 'module';
var x = m(someoptions);

或者,如果您使用支持 monadic 承诺的模块加载器,您也许可以执行类似的操作

System.import('module').ap(someoptions).then(function(x) 
    …
);

有了新的import operator,它可能会变成

const promise = import('module').then(m => m(someoptions));

const x = (await import('module'))(someoptions)

但是您可能不想要动态导入而是静态导入。

【讨论】:

谢谢,我希望有类似import x from 'module' use someoptions; 的语法 @Fabrizio:如果您进一步考虑,它实际上并没有那么有用。它仅在模块导出函数时才有效,并且如果我们命名为导入(即import x, y from 'module')可能不应该被允许。那么如果我想传递多个参数,语法应该是什么?或者传播一系列论点?这是一个狭窄的用例,基本上您正在尝试为函数调用添加不同的语法,但我们已经有了函数调用,可以让我们处理所有其他情况。 @FelixKling 我完全同意你的看法。我正在转换一个旧的 express webapp,遇到var session = require('express-session'); var RedisStore = require('connect-redis')(session); 我只是想知道是否有单行解决方案。我完全可以通过将 RedisStore 分配分成两行来生存:) @FabrizioGiordano:如果真的需要的话,我可以在 ES7 中想象像 import default(someoptions) as x from 'module' 这样的东西。 对于session/connect-redis 的例子,我一直在想象这样的语法:import session from 'express-session'); import RedisStore(session) from 'connect-redis'【参考方案2】:

概念

这是我使用 ES6 的解决方案

非常符合@Bergi 的响应,这是我在创建需要为class 声明传递参数的导入时使用的“模板”。这用于我正在编写的同构框架,因此可以在浏览器和 node.js 中使用转译器(我使用 BabelWebpack):

./MyClass.js

export default (Param1, Param2) => class MyClass 
    constructor()
        console.log( Param1 );
    

./main.js

import MyClassFactory from './MyClass.js';

let MyClass = MyClassFactory('foo', 'bar');

let myInstance = new MyClass();

上面会在控制台输出foo

编辑

现实世界的例子

对于一个真实世界的例子,我使用它来传递一个命名空间来访问框架中的其他类和实例。因为我们只是创建一个函数并将对象作为参数传入,所以我们可以将它与我们的类声明一起使用,如下所示:

export default (UIFramework) => class MyView extends UIFramework.Type.View 
    getModels() 
        // ...
        UIFramework.Models.getModelsForView( this._models );
        // ...
    

考虑到它是一个完整的框架,导入有点复杂,automagical 在我的情况下,但基本上这就是正在发生的事情:

// ...
getView( viewName )
    //...
    const ViewFactory = require(viewFileLoc);
    const View = ViewFactory(this);
    return new View();

// ...

我希望这会有所帮助!

【讨论】:

既然你导入的模块都是类,为什么在实例化类的时候不传参数呢? @jasonszhao 这里要注意的最重要的一点是MyView 类扩展了框架命名空间中可用的某些项目。虽然绝对可以简单地将它作为参数传递给类,但它还取决于类被实例化的时间和地点;便携性就会受到影响。在实践中,这些类可能会被移交给可能以不同方式实例化它们的其他框架(例如,自定义 React 组件)。当类发现自己在框架范围之外时,由于这种方法,它仍然可以在实例化时保持对框架的访问。 @Swivel 请协助我在类似问题上需要帮助:***.com/questions/55214957/…【参考方案3】:

在 @Bergi 的 answer 的基础上使用 es6 使用 debug module 如下

// original
var debug = require('debug')('http');

// ES6
import * as Debug from 'debug';
const debug = Debug('http');

// Use in your code as normal
debug('Hello World!');

【讨论】:

typescript with "module": "commonjs" and "esModuleInterop": true in tsconfig.js - import * as createPrompt from '../node_modules/prompt-sync'; const prompt = (createPrompt.default ?? createPrompt)(); 所以这适用于 tsc 和 ts-node 命令【参考方案4】:

我相信你可以使用 es6 模块加载器。 http://babeljs.io/docs/learn-es6/

System.import("lib/math").then(function(m) 
  m(youroptionshere);
);

【讨论】:

但是m(youroptionshere) 的结果在哪里结束?我想你可以写System.import('lib/math').then(m => m(options)).then(module => /* code using module here */)... 但不是很清楚。 哇,我不敢相信在 E6 中没有优雅的方法可以做到这一点。这就是我主要编写模块的方式。【参考方案5】:

你只需要添加这两行。

import xModule from 'module';
const x = xModule('someOptions');

【讨论】:

这只是将参数传递给您已导入并正在调用的函数。它不会将任何选项传递给您从中导入它的模块xModule 在这里具有误导性。你实际拥有的是import func from 'module'; func('someOptions');【参考方案6】:

我已经登陆这个线程,寻找一些类似的东西,并想提出一种解决方案,至少在某些情况下(但请参阅下面的备注)。

用例

我有一个模块,它在加载后立即运行一些实例化逻辑。我确实喜欢在模块外部调用这个初始化逻辑(与调用 new SomeClass(p1, p2)new ((p1, p2) => class SomeClass ... p1 ... p2 ... ) 等类似)。

我确实喜欢这个初始化逻辑将运行一次,有点像一个单一的实例化流程,但是每个特定的参数化上下文运行一次。

示例

service.js 具有最基本的范围:

let context = null;                  // meanwhile i'm just leaving this as is
console.log('initialized in context ' + (context ? context : 'root'));

模块 A:

import * as S from 'service.js';     // console has now "initialized in context root"

模块 B 的作用:

import * as S from 'service.js';     // console stays unchanged! module's script runs only once

到目前为止一切顺利:两个模块都可以使用服务,但只初始化了一次。

问题

如何让它作为另一个实例运行并在另一个上下文中再次初始化自己,比如在模块 C 中?

解决方案?

这就是我正在考虑的:使用查询参数。在服务中,我们将添加以下内容:

let context = new URL(import.meta.url).searchParams.get('context');

模块 C 可以:

import * as S from 'service.js?context=special';

模块将被重新导入,它的基本初始化逻辑将运行,我们将在控制台中看到:

initialized in context special

备注:我自己建议不要过多地练习这种方法,而是将其作为最后的手段。为什么?多次导入的模块更多的是例外而不是规则,因此它有些出乎意料的行为,因此可能会使消费者感到困惑甚至破坏它自己的“单例”范式(如果有的话)。

【讨论】:

【参考方案7】:

以下是我对这个问题的看法,以调试模块为例;

在这个模块的 npm 页面上,你有这个:

var debug = require('debug')('http')

在上面的行中,一个字符串被传递给导入的模块,以进行构造。下面是你在 ES6 中如何做同样的事情


从“调试”导入 调试为调试 const debug = Debug('http');


希望这对那里的人有所帮助。

【讨论】:

为什么要发布与already posted one 重复的答案? 我的错。从来没有看过提到的帖子。只是看了这个问题,然后刺了一下。感谢您提醒我。 不客气。如果您愿意,也可以删除重复的答案。【参考方案8】:

我在尝试将一些 CJS (require()) 代码转换为 ESM (import) 时遇到了类似的语法问题 - 这是我需要导入 Redis 时的工作:

CJS

const RedisStore = require('connect-redis')(session);

等效 ESM

import connectRedis from 'connect-redis';
const RedisStore = connectRedis(session);

【讨论】:

以上是关于将选项传递给 ES6 模块导入的主要内容,如果未能解决你的问题,请参考以下文章

无法将 ES6 模块导入 Vue 单文件组件

如何使用 es6 导入加载 emscripten 生成的模块?

d3 4.x 的 es6 模块导入失败

Typescript:esnext 编译器选项会破坏从外部库导入的 es6

如何将 ES6 导入与“请求”npm 模块一起使用

如何使用 ES6 模块导入来导入路径