将选项传递给ES6模块导入


144

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

您如何翻译此:

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

到ES6?


不确定是否可以,是否有模块加载器API,或者至少在某个时候使用了类似的东西System.import(module),不确定是否允许自变量,对ES6了解更多的人可能呢?
adeneo 2015年

有一个针对此的建议解决方案,为此已经在node.js(通过插件)和webpack中实现了:2ality.com/2017/01/import-operator.html
Matt Browne

Answers:


104

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

因此,您不会直接调用它,但是基本上可以执行与commonjs默认导出相同的操作:

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

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

另外,如果您使用支持单价承诺的模块加载器,则可以执行类似

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

有了新的import运营商,它可能会变成

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

要么

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

但是,您可能不希望动态导入,而希望静态导入。


7
谢谢,我希望import x from 'module' use someoptions;语法有点像
Fabrizio Giordano

1
@Fabrizio:如果您进一步考虑,那真的没有那么大的帮助。它仅在模块导出函数时才起作用,并且如果我们已命名imports(即import {x, y} from 'module'),则可能不应该被允许。如果我想传递多个参数,语法应该是什么?还是散布参数?这是一个狭窄的用例,基本上,您正在尝试为函数调用添加不同的语法,但是我们已经有了函数调用,可以让我们处理所有其他情况。
Felix Kling 2015年

3
@FelixKling我完全同意你的看法。我正在转换一个旧的Express Webapp,遇到的是var session = require('express-session'); var RedisStore = require('connect-redis')(session);我想知道是否有一个单一的解决方案。通过将RedisStore分配为两行,我可以完全生存下来:)
Fabrizio Giordano

@FabrizioGiordano:import {default(someoptions) as x} from 'module'如果确实需要这样做,我可以想像ES7中的情况。
Bergi 2015年

2
对于session/ connect-redis例如,我一直在想象的语法如下:import session from 'express-session'); import RedisStore(session) from 'connect-redis'
杰夫·汉德利

24

概念

这是我使用ES6的解决方案

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

./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

1
@jasonszhao这里要注意的最重要的一点是,该类MyView扩展了框架名称空间中可用的某些项。虽然绝对有可能简单地将其作为参数传递给类,但这也取决于实例化的时间和位置。然后,便携性受到影响。实际上,这些类可以移交给其他框架,这些框架可以以不同的方式实例化它们(例如,定制的React组件)。当类发现自己在框架范围之外时,由于这种方法,它在实例化时仍可以保持对框架的访问。
旋转

@Swivel请协助我需要类似问题的帮助:stackoverflow.com/questions/55214957/…–
TSR


4

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

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

3
但是结果到底在哪里m(youroptionshere)呢?我想你可以写System.import('lib/math').then(m => m(options)).then(module => { /* code using module here */})...但是还不太清楚。
Stijn de Witt

2
哇,我无法相信E6中没有优雅的方法可以做到这一点。那就是我主要编写模块的方式。
罗伯特·莫斯克

3

您只需要添加这两行。

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

1
这只是将参数传递给已导入并正在调用的函数。它没有将任何选项传递给您从中导入它的模块xModule在这里误导。你真正拥有的是import func from 'module'; func('someOptions');
Dan Dascalescu

1

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

用例

我有一个模块,该模块在加载后立即运行一些实例化逻辑。我喜欢在模块外部调用此初始化逻辑(这与call 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

备注:我本人建议不要过多地实践此方法,而应将其作为最后的选择。为什么?多次导入模块是一个例外,而不是一条规则,因此,这在某种程度上是意料之外的行为,因此可能会使使用者感到困惑,甚至破坏它自己的“单一”范式(如果有)。


0

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

在此模块的npm页面上,您具有以下内容:

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

在上面的行中,将一个字符串传递到导入的模块以进行构造。这是您在ES6中执行的操作


从'debug'导入{debug as Debug} const debug = Debug('http');


希望这可以帮助某人。


为什么要发布与已发布的答案重复的答案?
Dan Dascalescu

1
我的错。从未见过提到的帖子。只是看着这个问题,然后刺了一下它。感谢您通知我。
Akinwale Folorunsho Habib

别客气。您也可以根据需要删除重复的答案。
Dan Dascalescu
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.