ES6: Conditional & Dynamic Import Statements
Asked Answered
S

5

112

Conditional

Is it possible to have conditional import statements like below?

if (foo === bar) {
    import Baz from './Baz';
}

I have tried the above but get the following error (from Babel) when compiling.

'import' and 'export' may only appear at the top level

Dynamic

Is it possible to have dynamic import statements like below?

for (let foo in bar) {
    if (bar.hasOwnProperty(foo)) {
        import Baz from `./${foo}`;
    }
}

The above receives the same error from Babel whilst compiling.

Is this possible to do or is there something I am missing?

Reasoning

The reason I am trying to do this is that I have a lot of imports for a number of "pages" and they follow a similar pattern. I would like to clean up my code base by importing these files with a dynamic for loop.

If this is not possible then is there a better way to handle large number of imports in ES6?

Sell answered 10/3, 2016 at 11:8 Comment(6)
can't inheritance be used in such case? use super to call specific.Litchfield
I am already using inheritance, but these "pages" contain "page" specific logic in them. I do have a base "page" class that all extend but this is not enough to clean up the vast number of imports I have.Sell
@zerkms: They're not hoisted out of blocks - they're syntax errors.Declared
possible duplicate of ES6 variable import name in node.js?Declared
related: Generating es6 module exportsDeclared
also related / possible duplicate: #36368032Conversation
P
75

We do have dynamic imports proposal now with ECMA. This is in stage 2. This is also available as babel-preset.

Following is way to do conditional rendering as per your case.

if (foo === bar) {
    import('./Baz')
    .then((Baz) => {
       console.log(Baz.Baz);
    });
}

This basically returns a promise. Resolution of promise is expected to have the module. The proposal also has things like multiple dynamic imports, default imports, js file import etc. You can find more information about dynamic imports here.

Piaffe answered 3/10, 2017 at 11:51 Comment(2)
This. Dynamic imports are the way to go. They work just like a require(), except they give you a promise rather than a module.Gown
So this approach is finally in! See the more modern answer by @LeeGoddard for more info on thisCorena
S
27

You can't resolve dynamically your dependencies, as imports are meant for static analysis. However, you can probably use some require here, something like:

for (let foo in bar) {
    if (bar.hasOwnProperty(foo)) {
        const Baz = require(foo).Baz;
    }
}
Salk answered 10/3, 2016 at 11:13 Comment(4)
"as imports are meant for static analysis. " --- this statement is vague. imports are designed to import, not for analysis.Braxy
@Braxy - I think what they meant is that import statements are meant to be suitable for static analysis - because they're never conditional, tools can analyse the dependency trees easier.Benediction
Hard to understand with "foo" "baz" and "bar" - how about a real life example?Reiterant
This is no longer true. Dynamic imports are now a thing. See here: https://mcmap.net/q/194195/-es6-conditional-amp-dynamic-import-statementsGown
N
23

As this question is highly-ranked by Google, it is worth pointing out that things have changed since the older answers were posted.

MDN has this entry under 'Dynamic Imports':

The import keyword may be called as a function to dynamically import a module. When used this way, it returns a promise.

import('/modules/my-module.js')
  .then((module) => {
    // Do something with the module.
  });

This form also supports the await keyword.

let module = await import('/modules/my-module.js');

MDN also has a more detailed explanation.

A useful article on the subject can be found on Medium.

Nashville answered 27/1, 2020 at 9:43 Comment(2)
2022 update: MDN docs now have a separate entry for dynamic imports with more great examples (like conditional imports): developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Corena
it seems to, but it doesn't ;)Corena
T
11

Since 2016 a lot has passed in JavaScript world, so I believe it's time to offer most updated info on this topic. Currently Dynamic imports are a reality both on Node and on browsers (natively if you don't care about IE, or with @babel/plugin-syntax-dynamic-import if you do care).

So, consider a sample module something.js with two named exports and one default export:

export const hi = (name) => console.log(`Hi, ${name}!`)
export const bye = (name) => console.log(`Bye, ${name}!`)
export default () => console.log('Hello World!')

We can use import() syntax to easily and cleanly load it conditionally:

if (somethingIsTrue) {
  import('./something.js').then((module) => {
    // Use the module the way you want, as:
    module.hi('Erick') // Named export
    module.bye('Erick') // Named export
    module.default() // Default export
  })
}

But since the return is a Promise, the async/await syntactic sugar is also possible:

async imAsyncFunction () {
  if (somethingIsTrue) {
    const module = await import('./something.js')
    module.hi('Erick')
  }
}

Now think about the possibilities along with Object Destructuring Assignment! For example, we are able to easily put only one of those named exports in memory for posterior use:

const { bye } = await import('./something.js')
bye('Erick')

Or maybe grab one of those named exports and rename it to anything else we want:

const { hi: hello } = await import('./something.js')
hello('Erick')

Or even rename the default exported function to something that makes more sense:

const { default: helloWorld } = await import('./something.js')
helloWorld()

Just a last (but no least) note: import() may looks like a function call, but it isn't a Function. It's a special syntax that just happens to use parentheses (similar to what happens with super()). So it's not possible to assign import to a variable or use things of the Function prototype, like call/apply.

Tumid answered 5/10, 2020 at 12:0 Comment(0)
B
1

Require will not solve your problem as it is a synchronous call. There are several options and they all involve

  1. Asking for the module you need
  2. Waiting for a promise to return the module

In ECMA Script there is support for lazy loading modules using SystemJS. This of course isn't supported in all browsers, so in the meantime you can use JSPM or a SystemJS shim.

https://github.com/ModuleLoader/es6-module-loader

Bohaty answered 11/5, 2016 at 18:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.