Comment puis-je importer sous condition un module ES6?

194

J'ai besoin de faire quelque chose comme:

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

Le code ci-dessus ne compile pas; il jette SyntaxError: ... 'import' and 'export' may only appear at the top level.

J'ai essayé d'utiliser System.importcomme indiqué ici , mais je ne sais pas d'où Systemvient. Est-ce une proposition ES6 qui n'a finalement pas été acceptée? Le lien vers "API programmatique" de cet article me renvoie vers une page de documentation obsolète .

ericsoco
la source
Importez-le simplement normalement. Votre module en a quand même besoin.
Andy
Je ne vois vraiment aucune raison pour laquelle vous n'importeriez pas simplement quelle que soit la condition. Ce n'est pas comme s'il y avait une sorte de frais généraux. Dans certains scénarios, vous avez besoin du fichier, donc ce n'est pas comme s'il y avait un cas où il peut être entièrement ignoré. Dans ce cas, importez-le simplement sans condition.
Merci le
8
Mon cas d'utilisation: je souhaite faciliter la création d'une dépendance facultative. Si le dépôt n'est pas nécessaire, l'utilisateur le supprime package.json; my gulpfilevérifie ensuite si cette dépendance existe avant d'effectuer certaines étapes de construction.
ericsoco
1
Un autre cas d'utilisation: à des fins de test. J'utilise webpacket babeltranspile es6 en es5. Les projets comme webpack-rewireet similaires ne sont d'aucune utilité ici - github.com/jhnns/rewire-webpack/issues/12 . Une façon de définir les doubles de test OU de supprimer les dépendances problématiques pourrait être l'importation conditionnelle.
Amio.io
3
+1. Être capable d'utiliser un module dans plusieurs environnements où les dépendances peuvent ou non fonctionner est essentiel, en particulier lorsque les modules peuvent faire référence à des dépendances qui ne fonctionneraient que dans le navigateur (par exemple, où webpackest utilisé pour convertir les feuilles de style en modules qui insèrent les styles pertinents dans le DOM lorsqu'ils sont importés) mais le module doit également s'exécuter en dehors du navigateur (par exemple pour les tests unitaires).
Jules

Réponses:

146

Nous avons maintenant une proposition d'importation dynamique avec l'ECMA. Ceci est à l'étape 3. Ceci est également disponible en tant que babel-preset .

Voici un moyen de faire un rendu conditionnel selon votre cas.

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

Cela renvoie essentiellement une promesse. La résolution de la promesse devrait avoir le module. La proposition a également d'autres fonctionnalités comme les importations dynamiques multiples, les importations par défaut, l'importation de fichiers js, etc. Vous pouvez trouver plus d'informations sur les importations dynamiques ici .

la prise de code
la source
13
Enfin, une vraie réponse ES6! Merci @thecodejack. En fait, au stade 3 au moment de la rédaction de cet article, selon cet article maintenant.
ericsoco
5
ou si vous venez de nommer des exportations vous pouvez déstructurer:if (condition) { import('something') .then(({ somethingExported }) => { console.log(somethingExported); }); }
ivn
4
sur Firefox et en cours d'exécution, npm run buildj'obtiens toujours l'erreur:SyntaxError: ... 'import' and 'export' may only appear at the top level
ste
1
@stackjlei: Cette fonctionnalité ne fait pas encore partie du standard JavaScript, c'est juste une proposition de stage 3! Cependant, il est déjà implémenté dans de nombreux navigateurs plus récents, voir caniuse.com/#feat=es6-module-dynamic-import .
Konrad Höffner
1
Cette fonction d'importation dynamique conditionnelle n'a pas la capacité fine d'importer uniquement des éléments particuliers que possède "importer X depuis Y". En fait, cette capacité à grain fin pourrait être encore plus importante dans le chargement dynamique (par opposition au regroupement de pré-processus)
Craig Hicks
102

Si vous le souhaitez, vous pouvez utiliser require. C'est une manière d'avoir une instruction conditionnelle require.

let something = null;
let other = null;

if (condition) {
    something = require('something');
    other = require('something').other;
}
if (something && other) {
    something.doStuff();
    other.doOtherStuff();
}
BaptWaels
la source
1
Je pense que quelque chose et d'autres variables sont décrétés en utilisant const qui est à portée de bloc, donc la deuxième condition if va jeter que quelque chose n'est pas défini
Mohammed Essehemy
Il vaudrait mieux utiliser let et déclarer les deux variables en dehors du bloc au lieu d'utiliser «var» et d'éviter complètement la portée du bloc.
Vorcan
Le levage affecte-t-il quelque chose dans ce cas? J'ai rencontré des problèmes où le levage a signifié que j'ai importé une bibliothèque par inadvertance en suivant un modèle proche de celui-ci si ma mémoire est bonne.
Thomas
11
Il faut souligner que cela require()ne fait pas partie de JavaScript standard - c'est une fonction intégrée dans Node.js, donc seulement utile dans cet environnement. L'OP ne donne aucune indication de travailler avec Node.js.
Velojet
56

Vous ne pouvez pas importer de manière conditionnelle, mais vous pouvez faire le contraire: exporter quelque chose de manière conditionnelle. Cela dépend de votre cas d'utilisation, donc ce travail n'est peut-être pas pour vous.

Tu peux faire:

api.js

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

const exportedAPI = shouldUseMock ? mockAPI : realAPI
export default exportedAPI

apiConsumer.js

import API from './api'
...

J'utilise cela pour simuler des bibliothèques d'analyse comme mixpanel, etc. parce que je ne peux pas avoir plusieurs builds ou notre frontend actuellement. Pas le plus élégant, mais fonctionne. J'ai juste quelques «si» ici et là en fonction de l'environnement car dans le cas du mixpanel, il a besoin d'être initialisé.

Kev
la source
40
Cette solution provoque le chargement de modules indésirables, donc pas une solution optimale, je pense.
ismailarilik
5
Comme indiqué dans la réponse, il s'agit d'une solution de contournement. À cette époque, il n'y avait tout simplement pas de solution. Les importations ES6 ne sont pas dynamiques, c'est par conception. La proposition de fonction d'importation dynamique ES6, qui est décrite dans la réponse actuellement acceptée, peut le faire. JS évolue :)
Kev
9

On dirait que la réponse est que, pour le moment, vous ne pouvez pas.

http://exploringjs.com/es6/ch_modules.html#sec_module-loader-api

Je pense que l'intention est de permettre l'analyse statique autant que possible, et les modules importés conditionnellement brisent cela. Il convient également de mentionner - j'utilise Babel , et je suppose que cela Systemn'est pas pris en charge par Babel car l'API du chargeur de module n'est pas devenue une norme ES6.

ericsoco
la source
@FelixKling en fait sa propre réponse et je l'accepterai avec plaisir!
ericsoco
4

require()est un moyen d'importer un module au moment de l'exécution et il se qualifie également pour l'analyse statique, comme imports'il était utilisé avec des chemins littéraux de chaîne. Ceci est requis par le bundler pour choisir les dépendances pour le bundle.

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

Pour une résolution de module dynamique avec prise en charge complète de l'analyse statique, commencez par indexer les modules dans un indexeur (index.js) et importez l'indexeur dans le module hôte.

// 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]);
Shoaib Nawaz
la source
7
Il faut souligner que cela require()ne fait pas partie de JavaScript standard - c'est une fonction intégrée dans Node.js, donc seulement utile dans cet environnement. L'OP ne donne aucune indication de travailler avec Node.js.
Velojet
2

Différence importante si vous utilisez le mode d'importation dynamique 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(...);
}
Solo
la source
Mais importrenvoie une promesse.
newguy
0

l'obscurcir dans une évaluation a fonctionné pour moi, le cachant de l'analyseur statique ...

if (typeof __CLI__ !== 'undefined') {
  eval("require('fs');")
}
Chris Marstall
la source
3
Quelqu'un peut-il expliquer pourquoi cette réponse a été rejetée? Y a-t-il de réels inconvénients ou était-ce juste une réaction négative automatique au mot-clé maléfique 'eval'?
Yuri Gor
3
Downvote automatique pour l'utilisation du mot-clé hideux eval. Reste loin.
Tormod Haugene
1
Pouvez-vous expliquer ce qui ne va pas avec l'utilisation d' evalici, @TormodHaugene?
Adam Barnes
MDN résume plusieurs raisons pour lesquelles evalil ne devrait pas être utilisé . En général: si vous trouvez le besoin d'utiliser eval, vous le faites probablement mal et devriez prendre du recul pour envisager vos alternatives. Il existe probablement des scénarios où l'utilisation evalest correcte, mais vous n'avez probablement pas rencontré l'une de ces situations.
Tormod Haugene
5
Il faut souligner que cela require()ne fait pas partie de JavaScript standard - c'est une fonction intégrée dans Node.js, donc seulement utile dans cet environnement. L'OP ne donne aucune indication de travailler avec Node.js.
Velojet
0

J'ai pu y parvenir en utilisant une fonction immédiatement appelée et une instruction require.

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

if(something) {
  something.doStuff();
}
bradley2w1dl
la source
5
Il faut souligner que cela require()ne fait pas partie de JavaScript standard - c'est une fonction intégrée dans Node.js, donc seulement utile dans cet environnement. L'OP ne donne aucune indication de travailler avec Node.js.
Velojet
0

Les importations conditionnelles pourraient également être réalisées avec un ternaire et require()s:

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

Cet exemple est tiré de la documentation ES Lint global-require: https://eslint.org/docs/rules/global-require

Livre Elliot
la source
5
Il faut souligner que cela require()ne fait pas partie de JavaScript standard - c'est une fonction intégrée dans Node.js, donc seulement utile dans cet environnement. L'OP ne donne aucune indication de travailler avec Node.js.
Velojet
0

Non, tu ne peux pas!

Cependant, après avoir rencontré ce problème, vous devriez repenser la façon dont vous organisez votre code.

Avant les modules ES6, nous avions des modules CommonJS qui utilisaient la syntaxe require (). Ces modules étaient «dynamiques», ce qui signifie que nous pouvions importer de nouveaux modules en fonction des conditions de notre code. - source: https://bitsofco.de/what-is-tree-shaking/

Je suppose qu'une des raisons pour lesquelles ils ont abandonné ce support sur ES6 est le fait que le compiler serait très difficile, voire impossible.

Aldee
la source