Passer des options aux importations de modules ES6

144

Est-il possible de transmettre des options aux importations ES6?

Comment traduisez-vous ceci:

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

à ES6?

Fabrizio Giordano
la source
Vous n'êtes pas sûr de pouvoir, il y a une API de chargeur de module, ou du moins il y en avait à un moment donné, qui utilisait quelque chose comme System.import(module), je ne sais pas si cela permet des arguments ou non, quelqu'un qui en sait plus sur ES6 le fait probablement?
adeneo
Il existe une solution proposée pour cela, pour laquelle il existe déjà des implémentations dans node.js (via un plugin) et webpack: 2ality.com/2017/01/import-operator.html
Matt Browne

Réponses:

104

Il n'y a aucun moyen de faire cela avec une seule importinstruction, cela n'autorise pas les appels.

Donc, vous ne l'appelleriez pas directement, mais vous pouvez en gros faire exactement la même chose que commonjs avec les exportations par défaut:

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

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

Alternativement, si vous utilisez un chargeur de module qui prend en charge les promesses monadiques , vous pourrez peut-être faire quelque chose comme

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

Avec le nouvel importopérateur, il pourrait devenir

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

ou

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

Cependant, vous ne voulez probablement pas une importation dynamique mais une importation statique.

Bergi
la source
7
Merci, j'aurais aimé qu'il y ait quelque chose comme une sorte import x from 'module' use someoptions;de syntaxe
Fabrizio Giordano
1
@Fabrizio: Si vous y réfléchissez plus loin, ce ne serait pas vraiment utile. Cela ne fonctionnerait que si le module exporte une fonction et ne devrait probablement pas être autorisé si nous avons nommé importations (ie import {x, y} from 'module'). Alors, quelle devrait être la syntaxe si je veux passer plusieurs arguments? Ou répandre un éventail d'arguments? C'est un cas d'utilisation restreint et en gros vous essayez d'ajouter une syntaxe différente pour un appel de fonction, mais nous avons déjà des appels de fonction qui nous permettent de traiter tous les autres cas.
Felix Kling
3
@FelixKling Je suis entièrement d'accord avec vous. J'étais en train de convertir une ancienne application Web express et je var session = require('express-session'); var RedisStore = require('connect-redis')(session);me suis rendu compte que je me demandais simplement s'il y avait une solution en une ligne. Je peux totalement survivre avec la répartition de la mission RedisStore en 2 lignes :)
Fabrizio Giordano
@FabrizioGiordano: Je pourrais imaginer quelque chose comme import {default(someoptions) as x} from 'module'dans ES7, s'il y a vraiment un besoin pour cela.
Bergi
2
Pour le session/ connect-redisexemple, je suis imaginer la syntaxe comme ceci: import session from 'express-session'); import RedisStore(session) from 'connect-redis'.
Jeff Handley
24

Concept

Voici ma solution utilisant ES6

Tout à fait conforme à la réponse de @ Bergi, c'est le "modèle" que j'utilise lors de la création d'importations qui nécessitent des paramètres passés pour les classdéclarations. Ceci est utilisé sur un framework isomorphe que j'écris, donc fonctionnera avec un transpilateur dans le navigateur et dans node.js (j'utilise Babelavec 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();

Ce qui précède sortira foodans une console

ÉDITER

Exemple du monde réel

Pour un exemple du monde réel, j'utilise ceci pour passer dans un espace de noms pour accéder à d'autres classes et instances dans un cadre. Parce que nous créons simplement une fonction et que nous transmettons l'objet en tant qu'argument, nous pouvons l'utiliser avec notre déclaration de classe likeo:

export default (UIFramework) => class MyView extends UIFramework.Type.View {
    getModels() {
        // ...
        UIFramework.Models.getModelsForView( this._models );
        // ...
    }
}

L'importation est un peu plus compliquée et automagicaldans mon cas étant donné que c'est un framework entier, mais c'est essentiellement ce qui se passe:

// ...
getView( viewName ){
    //...
    const ViewFactory = require(viewFileLoc);
    const View = ViewFactory(this);
    return new View();
}
// ...

J'espère que ça aide!

Pivot
la source
Puisque tous vos modules importés sont des classes, pourquoi ne pas passer le paramètre lors de l'instanciation de la classe?
jasonszhao
1
@jasonszhao La chose la plus importante à noter ici est que la classe MyViewétend certains éléments disponibles dans l'espace de noms du framework. Bien qu'il soit absolument possible de le transmettre simplement en tant que paramètre à la classe, cela dépend également du moment et de l'endroit où la classe est instanciée; la portabilité est alors affectée. En pratique, ces classes peuvent être transférées à d'autres frameworks qui peuvent les instancier différemment (par exemple, des composants React personnalisés). Lorsque la classe se trouve hors de la portée du framework, elle peut toujours conserver l'accès au framework lorsqu'elle est instanciée en raison de cette méthodologie.
Pivoter
@Swivel S'il vous plaît aider J'ai besoin d'aide avec un problème similaire: stackoverflow.com/questions/55214957/…
TSR
12

S'appuyant sur la réponse de @ Bergi pour utiliser le module de débogage en utilisant es6 serait le suivant

// original
var debug = require('debug')('http');

// ES6
import * as Debug from 'debug';
const debug = Debug('http');

// Use in your code as normal
debug('Hello World!');
momie
la source
4

Je pense que vous pouvez utiliser des chargeurs de module es6. http://babeljs.io/docs/learn-es6/

System.import("lib/math").then(function(m) {
  m(youroptionshere);
});
user895715
la source
3
Mais où m(youroptionshere)finit le résultat ? Je suppose que vous pourriez écrire System.import('lib/math').then(m => m(options)).then(module => { /* code using module here */})... mais ce n'est pas très clair.
Stijn de Witt
2
Wow, je ne peux pas croire qu'il n'y a pas de moyen élégant de faire cela dans E6. C'est comme ça que j'écris principalement des modules.
Robert Moskal
3

Il vous suffit d'ajouter ces 2 lignes.

import xModule from 'module';
const x = xModule('someOptions');
Mansi Teharia
la source
1
Il s'agit simplement de transmettre des paramètres à une fonction que vous avez importée et que vous appelez. Il ne transmet aucune option au module à partir duquel vous l'importez . xModuleest trompeur ici. Ce que vous avez réellement est import func from 'module'; func('someOptions');.
Dan Dascalescu du
1

J'ai atterri sur ce fil à la recherche d'un peu similaire et j'aimerais proposer une sorte de solution, au moins pour certains cas (mais voir la remarque ci-dessous).

Cas d'utilisation

J'ai un module, qui exécute une logique d'instanciation immédiatement lors du chargement. Je n'aime pas appeler cette logique d'initialisation en dehors du module (ce qui revient à appeler new SomeClass(p1, p2)ounew ((p1, p2) => class SomeClass { ... p1 ... p2 ... }) and alike).

J'aime le fait que cette logique init s'exécute une fois, une sorte de flux d'instanciation singulier, mais une fois par contexte paramétré spécifique.

Exemple

service.js a à sa portée très basique:

let context = null;                  // meanwhile i'm just leaving this as is
console.log('initialized in context ' + (context ? context : 'root'));

Le module A fait:

import * as S from 'service.js';     // console has now "initialized in context root"

Le module B fait:

import * as S from 'service.js';     // console stays unchanged! module's script runs only once

Jusqu'ici tout va bien: le service est disponible pour les deux modules mais n'a été initialisé qu'une seule fois.

Problème

Comment le faire fonctionner comme une autre instance et l'initier à nouveau dans un autre contexte, par exemple dans le module C?

Solution?

C'est ce à quoi je pense: utiliser des paramètres de requête. Dans le service, nous ajouterions ce qui suit:

let context = new URL(import.meta.url).searchParams.get('context');

Le module C ferait:

import * as S from 'service.js?context=special';

le module sera réimporté, sa logique d'initialisation de base s'exécutera et nous verrons dans la console:

initialized in context special

Remarque: je conseillerais moi-même de NE PAS pratiquer beaucoup cette approche, mais de la laisser en dernier recours. Pourquoi? Le module importé plus d'une fois est plus une exception qu'une règle, il s'agit donc d'un comportement quelque peu inattendu et, en tant que tel, peut confondre un consommateur ou même casser ses propres paradigmes «singleton», le cas échéant.

GullerYA
la source
0

Voici mon point de vue sur cette question en utilisant le module de débogage comme exemple;

Sur la page npm de ce module, vous avez ceci:

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

Dans la ligne ci-dessus, une chaîne est passée au module importé, à construire. Voici comment vous feriez de même dans ES6


import {debug as Debug} depuis 'debug' const debug = Debug ('http');


J'espère que cela aide quelqu'un là-bas.

Akinwale Folorunsho Habib
la source
Pourquoi publier une réponse qui duplique une réponse déjà publiée ?
Dan Dascalescu
1
Ma faute. Jamais vu le message mentionné. J'ai juste regardé la question et j'ai tenté d'y répondre. Merci de l'avoir porté à ma connaissance.
Akinwale Folorunsho Habib
De rien. Vous pouvez également supprimer la réponse en double si vous le souhaitez.
Dan Dascalescu