node.js中的ES6变量导入名称?


108

使用ES6导入时是否可以将某些内容导入提供变量名称的模块中?

即我想根据配置中提供的值在运行时导入一些模块:

import something from './utils/' + variableName;

1
@Bigood是的,编译器抛出了,Webstorm也显示了一个错误
Vytautas Butkus

Answers:


67

不与import声明。importexport以可静态分析的方式进行定义,因此它们不能依赖于运行时信息。

您正在寻找loader API(polyfill),但是我对规范的状态尚不清楚:

System.import('./utils/' + variableName).then(function(m) {
  console.log(m);
});

3
我需要“需要”系统吗?它不在全球范围内。.PS我正在使用babel js
Vytautas Butkus

AFAIK尚未在任何地方实现。您必须使用链接中的polyfill。
Felix Kling 2015年

1
刚查了,它还挺作品(尝试加载文件),但是打乱了其他模块的路径...太糟糕了,它本身不支持..
维陶塔斯布特库斯

3
这还是个问题吗?我需要动态加载ES6模块,但没有成功
。.– calbertts

26

除了Felix的回答,我将明确指出,ECMAScript 6语法当前不允许这样做:

ImportDeclaration

  • import ImportClause FromClause;

  • 导入 ModuleSpecifier;

FromClause

  • 来自 ModuleSpecifier

ModuleSpecifier

  • 字符串字面量

ModuleSpecifier只能是一个串文字,而不是任何其他种类等的表达的AdditiveExpression


2
可惜的是它没有扩展到const string literals。它们是静态可分析的,不是吗?这将使重用依赖项的位置成为可能。(例如,导入模板,并同时提供模板和模板的位置)。
nicodemus13年

26

虽然这实际上不是动态导入(例如,在我的情况下,我要在下面导入的所有文件都将由webpack导入并捆绑在一起,而不是在运行时选择),但我一直在使用的一种模式在某些情况下可能会有所帮助:

import Template1 from './Template1.js';
import Template2 from './Template2.js';

const templates = {
  Template1,
  Template2
};

export function getTemplate (name) {
  return templates[name];
}

或者:

// index.js
export { default as Template1 } from './Template1';
export { default as Template2 } from './Template2';


// OtherComponent.js
import * as templates from './index.js'
...
// handy to be able to fall back to a default!
return templates[name] || templates.Template1;

我不认为我可以使用轻松地恢复为默认值require(),如果我尝试导入不存在的构造模板路径,则会引发错误。

可以在以下位置找到需求与导入之间的良好示例和比较:http : //www.2ality.com/2014/09/es6-modules-final.html

有关从@iainastacio重新导出的出色文档:http ://exploringjs.com/es6/ch_modules.html#sec_all-exporting-styles

我很想听听对此方法的反馈意见:)


赞成。我使用了“或替代”方法。对于我的自定义本地化解决方案来说,它就像是一种魅力。
groundh0g


1
我应该想到这个。出色的解决方案,无论您如何导入事物(即使您什么也没有导入)。是否有一个列表,您想稍后再获取名称或按名称获取?将它们放在对象文字中,而不是在数组文字中,并让对象语法根据它们的本地常量/变量名称来命名它们。如果您再次需要它们作为列表,请执行Object.values(templates)
安德鲁·科斯特

15

有一个新规范,称为ES模块动态导入。基本上,您只需打电话import('./path/file.js')就可以了。该函数返回一个promise,如果导入成功,则与模块一起解析。

async function importModule() {
   try {
      const module = await import('./path/module.js');
   } catch (error) {
      console.error('import failed');
   }
}

用例

用例包括针对React,Vue等的基于路由的组件导入,以及在运行时需要时,可以懒加载模块的能力。

更多信息

这是有关Google Developers的说明。

浏览器兼容性(2020年4月)

MDN称,当前所有主流浏览器(IE除外)支持该功能,并且caniuse.com在全球市场份额中显示87%的支持。同样,不支持IE或非铬Edge。


您确定要编辑吗?该提案显示了一个具有可变路径的示例:github.com/tc39/proposal-dynamic-import#example
phil294

我是@Blauhirn,但您的链接清楚地表明了这种可能性。虽然我不知道怎么的WebPack,例如将解决这些进口
尼古拉·施密德

如果我错了,请纠正我,但是webpack不会处理这些,对吗?我认为这是动态导入在浏览器中“按原样”运行的关键。
phil294

是的,您可以按原样在浏览器中运行它们。但是,webpack会自动使用导入将您的应用分为应用程序不同部分(例如路线)的多个包。我一直在使用它们,它们确实很有帮助。就“处理”而言;webpack将通过导入babel,这将为较旧的浏览器填充功能。
尼古拉·施密德

需要明确的是:动态import()与变量一起使用,并且不需要进行静态分析(当然,这就是“动态”的全部要点)。看我的答案。
Velojet

6

我了解importNode.js中特别针对ES6提出的问题,但以下内容可能会帮助其他人寻求更通用的解决方案:

let variableName = "es5.js";
const something = require(`./utils/${variableName}`);

请注意,如果要导入ES6模块并需要访问default导出,则将需要使用以下方法之一:

let variableName = "es6.js";

// Assigning
const defaultMethod = require(`./utils/${variableName}`).default;

// Accessing
const something = require(`./utils/${variableName}`);
something.default();

您还可以通过这种方法使用解构,这可能会使您对其他导入的语法更加熟悉:

// Destructuring 
const { someMethod } = require(`./utils/${variableName}`);    
someMethod();

不幸的是,如果要访问default和销毁结构,则需要分多个步骤执行:

// ES6 Syntax
Import defaultMethod, { someMethod } from "const-path.js";

// Destructuring + default assignment
const something = require(`./utils/${variableName}`);

const defaultMethod = something.default;    
const { someMethod, someOtherMethod } = something;

4

您可以使用非ES6表示法来实现。这对我有用:

let myModule = null;
if (needsToLoadModule) {
  myModule = require('my-module').default;
}

3

我不太喜欢这种语法,但是它可以工作:
而不是编写

import memberName from "path" + "fileName"; 
// this will not work!, since "path" + "fileName" need to be string literal

使用以下语法:

let memberName = require("path" + "fileName");

1
@UlysseBN不好的地方不同吗?还是不重要的方式?
山姆

@Jacob确实完全不同,所以是的,这取决于您在做什么。第一种语法是静态评估的,而第二种是动态评估的。因此,例如,如果您使用的是webpack,则不能与第二个一起正确地进行树状摇动。还有许多其他差异,建议您阅读该文档,看看哪一个更适合您!
Ulysse BN

@Jacob-没关系(在大多数情况下)。require()是用于加载文件的Node.JS方法,这是早期版本。importstatement是较新的版本,现在已成为官方语言语法的一部分。但是,在许多情况下,浏览器将使用以前的版本(在科学之后)。require语句还将兑现您的文件,因此,如果第二次加载文件,则会从内存中加载文件(性能更好)。导入方式有其自身的好处-如果您使用的是WebPack。然后webpack可以删除无效引用(这些脚本不会下载到客户端)。
Gil Epshtain '19

1

动态import()(在Chrome 63+中可用)可以完成您的工作。这是如何做:

let variableName = 'test.js';
let utilsPath = './utils/' + variableName;
import(utilsPath).then((module) => { module.something(); });

0

我会这样

function load(filePath) {
     return () => System.import(`${filePath}.js`); 
     // Note: Change .js to your file extension
}

let A = load('./utils/' + variableName)

// Now you can use A in your module

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.