如何有条件导入ES6模块?


192

我需要做类似的事情:

if (condition) {
    import something from 'something';
}
// ...
if (something) {
    something.doStuff();
}

上面的代码无法编译;它抛出SyntaxError: ... 'import' and 'export' may only appear at the top level

我尝试使用此处System.import所示的方法,但是我不知道从哪里来。这是没有最终被接受的ES6提案吗?那篇文章中指向“编程API”的链接将我转至不推荐使用的docs页面System


只需正常导入即可。无论如何,您的模块都需要它。
安迪

我真的看不出任何原因为何无论情况如何都不会仅仅导入。好像没有某种开销。在某些情况下,您需要该文件,因此,在任何情况下都不能完全跳过它。在这种情况下,只需无条件导入即可。
谢谢您

8
我的用例:我想简化具有可选依赖项的过程。如果不需要dep,则用户将其从package.json;中删除。gulpfile然后我会在执行一些构建步骤之前检查该依赖项是否存在。
ericsoco '16

1
另一个用例:用于测试目的。我正在使用webpack并将babeles6转换为es5。诸如此类的项目webpack-rewire在这里无济于事-github.com/jhnns/rewire-webpack/issues/12。设置测试倍数或删除有问题的依赖项的一种方法是条件导入。
Amio.io

3
+1。能够在依赖项可能起作用或可能不起作用的多个环境中使用模块至关重要,尤其是当模块可能引用仅在浏览器中起作用的依赖项时(例如,webpack用于将样式表转换为将相关样式插入到模块中的模块的地方)。导入时为DOM),但模块也需要在浏览器外部运行(例如,用于单元测试)。
Jules

Answers:


142

现在,ECMA确实有动态进口建议。这是在第3阶段。这也可以作为babel-preset使用

以下是根据您的情况进行条件渲染的方法。

if (condition) {
    import('something')
    .then((something) => {
       console.log(something.something);
    });
}

这基本上返回了一个承诺。承诺解决方案有望包含该模块。该提案还具有其他功能,例如多个动态导入,默认导入,js文件导入等。您可以在此处找到有关动态导入的更多信息。


13
最后,一个真实的ES6答案!谢谢@thecodejack。根据该文章,实际上在撰写本文时处于第3阶段。
ericsoco '17

5
或者,如果您刚刚命名了出口,则可以进行销毁:if (condition) { import('something') .then(({ somethingExported }) => { console.log(somethingExported); }); }
IVN

4
在Firefox上运行时,npm run build我仍然收到错误消息:SyntaxError: ... 'import' and 'export' may only appear at the top level
ste

测试失败,有人有想法吗?
stackjlei

1
有条件的动态导入功能不具有仅导入“从Y导入X”具有的特定元素的细粒度功能。实际上,细粒度的功能在动态加载中可能更为重要(与预处理捆绑相对)
Craig Hicks

101

如果需要,可以使用require。这是具有条件require语句的一种方式。

let something = null;
let other = null;

if (condition) {
    something = require('something');
    other = require('something').other;
}
if (something && other) {
    something.doStuff();
    other.doOtherStuff();
}

1
我认为使用块范围内的const来描述某些内容和其他变量,因此第二个if条件将抛出未定义的内容
Mohammed Essehemy 18-4-9

最好在块外使用let和声明两个变量,而不要使用'var'并完全避免块作用域。
Vorcan '18 -4-12

在这种情况下,起吊会不会有任何影响?我遇到了一些问题,在这些问题中,吊起意味着在遵循内存服务的情况下,我遵循这种模式意外导入了一个库。
汤玛斯(Thomas)

11
需要指出的是,require()它不是标准JavaScript的一部分-它是Node.js中的内置函数,因此仅在该环境中有用。OP没有提供使用Node.js的指示。
Velojet

53

您不能有条件地导入,但是可以做相反的事情:有条件地导出某些内容。这取决于您的用例,因此此解决方法可能不适合您。

你可以做:

api.js

import mockAPI from './mockAPI'
import realAPI from './realAPI'

const exportedAPI = shouldUseMock ? mockAPI : realAPI
export default exportedAPI

apiConsumer.js

import API from './api'
...

我用它来模拟诸如mixpanel等的分析库,因为我目前无法拥有多个版本或我们的前端。不是最优雅的,但有效。根据环境,我在这里和那里只有几个“ if”,因为在混合面板的情况下,它需要初始化。


39
我认为这种解决方案会导致加载不需要的模块,因此不是最佳解决方案。
ismailarilik

5
如答案中所述,这是一种解决方法。当时,根本没有解决方案。ES6导入不是动态的,这是设计使然。ES6动态导入功能建议(可以在当前接受的答案中进行描述)可以实现。JS正在发展:)
Kev


4

require()这是一种在运行时导入某些模块的方法,它同样可以进行静态分析(例如import与字符串文字路径一起使用)。捆绑程序需要此功能来为捆绑程序选择依赖项。

const defaultOne = require('path/to/component').default;
const NamedOne = require('path/to/component').theName;

对于具有完整静态分析支持的动态模块解析,首先在indexer(index.js)中建立索引模块,然后在主机模块中导入indexer。

// index.js
export { default as ModuleOne } from 'path/to/module/one';
export { default as ModuleTwo } from 'path/to/module/two';
export { SomeNamedModule } from 'path/to/named/module';

// host.js
import * as indexer from 'index';
const moduleName = 'ModuleOne';
const Module = require(indexer[moduleName]);

7
需要指出的是,require()它不是标准JavaScript的一部分-它是Node.js中的内置函数,因此仅在该环境中有用。OP没有提供使用Node.js的指示。
Velojet

2

如果使用动态导入Webpack模式,则重要区别在于eager

if (normalCondition) {
  // this will be included to bundle, whether you use it or not
  import(...);
}

if (process.env.SOMETHING === 'true') {
  // this will not be included to bundle, if SOMETHING is not 'true'
  import(...);
}

但是import回报了诺言。
newguy

0

在评估中使它模糊不清对我有用,将其隐藏在静态分析器中...

if (typeof __CLI__ !== 'undefined') {
  eval("require('fs');")
}

3
谁能解释为什么这个答案被否决了?是否有真正的弊端,还是对邪恶的关键字“ eval”的自动否定反应?
尤里·戈尔

3
自动下注以使用丑陋的eval关键字。远离。
Tormod Haugene

1
您能解释一下eval@TormodHaugene在这里的使用实际上有什么问题吗?
亚当·巴恩斯

MDN总结了许多eval不应使用的原因。一般而言:如果发现需要使用eval,则可能是做错了,应该退后一步考虑替代方案。在某些情况下使用eval正确是可能的,但是您很可能没有遇到过其中一种情况。
Tormod Haugene

5
需要指出的是,require()它不是标准JavaScript的一部分-它是Node.js中的内置函数,因此仅在该环境中有用。OP没有提供使用Node.js的指示。
Velojet

0

我能够使用立即调用的函数并要求语句来实现此目的。

const something = (() => (
  condition ? require('something') : null
))();

if(something) {
  something.doStuff();
}

5
需要指出的是,require()它不是标准JavaScript的一部分-它是Node.js中的内置函数,因此仅在该环境中有用。OP没有提供使用Node.js的指示。
Velojet

0

有条件的导入也可以通过三元和require()s实现:

const logger = DEBUG ? require('dev-logger') : require('logger');

该示例取自ES Lint的global-require文档:https : //eslint.org/docs/rules/global-require


5
需要指出的是,require()它不是标准JavaScript的一部分-它是Node.js中的内置函数,因此仅在该环境中有用。OP没有提供使用Node.js的指示。
Velojet


0

不,你不能!

但是,碰到这个问题会使您重新考虑如何组织代码。

在ES6模块之前,我们有CommonJS模块,它们使用require()语法。这些模块是“动态的”,这意味着我们可以根据代码中的条件导入新模块。-来源:https//bitsofco.de/what-is-tree-shaking/

我猜他们放弃对ES6的支持的原因之一是编译它非常困难或不可能的事实。

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.