module.exports vs export par défaut dans Node.js et ES6

318

Quelle est la différence entre les nœuds module.exportset les ES6 export default? J'essaie de comprendre pourquoi j'obtiens l'erreur "__ n'est pas un constructeur" lorsque j'essaie de le faire export defaultdans Node.js 6.2.2.

Ce qui fonctionne

'use strict'
class SlimShady {
  constructor(options) {
    this._options = options
  }

  sayName() {
    return 'My name is Slim Shady.'
  }
}

// This works
module.exports = SlimShady

Ce qui ne marche pas

'use strict'
class SlimShady {
  constructor(options) {
    this._options = options
  }

  sayName() {
    return 'My name is Slim Shady.'
  }
}

// This will cause the "SlimShady is not a constructor" error
// if in another file I try `let marshall = new SlimShady()`
export default SlimShady
Marty Chang
la source

Réponses:

402

Le problème est avec

  • comment les modules ES6 sont émulés dans CommonJS
  • comment importer le module

ES6 à CommonJS

Au moment d'écrire ces lignes, aucun environnement ne prend en charge les modules ES6 de manière native. Lorsque vous les utilisez dans Node.js, vous devez utiliser quelque chose comme Babel pour convertir les modules en CommonJS. Mais comment cela se produit-il exactement?

Beaucoup de gens considèrent module.exports = ...comme équivalents export default ...et exports.foo ...équivalents à export const foo = .... Ce n'est pas tout à fait vrai cependant, ou du moins pas comment Babel le fait.

Les defaultexportations ES6 sont en fait également nommées exportations, sauf qu'il defaults'agit d'un nom "réservé" et qu'il est pris en charge par une syntaxe spéciale. Voyons comment Babel compile les exportations nommées et par défaut:

// input
export const foo = 42;
export default 21;

// output
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
var foo = exports.foo = 42;
exports.default = 21; 

Ici, nous pouvons voir que l'exportation par défaut devient une propriété sur l' exportsobjet, tout comme foo.

Importez le module

Nous pouvons importer le module de deux manières: soit en utilisant CommonJS, soit en utilisant la importsyntaxe ES6 .

Votre problème: je crois que vous faites quelque chose comme:

var bar = require('./input');
new bar();

en attendant qu'on barlui attribue la valeur de l'exportation par défaut. Mais comme nous pouvons le voir dans l'exemple ci-dessus, l'exportation par défaut est affectée à la defaultpropriété!

Donc, pour accéder à l'exportation par défaut, nous devons réellement faire

var bar = require('./input').default;

Si nous utilisons la syntaxe du module ES6, à savoir

import bar from './input';
console.log(bar);

Babel le transformera en

'use strict';

var _input = require('./input');

var _input2 = _interopRequireDefault(_input);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

console.log(_input2.default);

Vous pouvez voir que chaque accès à barest converti en accès .default.

Felix Kling
la source
N'avons-nous pas un double pour cela?
Bergi
3
@Bergi: Je n'ai pas recherché tbh (honte à moi :(). Il y a certainement des questions sur le même problème, mais posées d'une manière différente. Faites-moi savoir si vous trouvez quelque chose qui vous convient!
Felix Kling
1
OK, il a fallu un certain temps pour les trouver, mais vous pouvez maintenant utiliser vos pouvoirs nouvellement acquis et choisir l'un des Comment utiliser correctement ES6 "export par défaut" avec CommonJS "require"? et ne peut pas exiger () la valeur d'exportation par défaut dans Babel 6.x comme cible de dupe :-)
Bergi
1
Quelle ironie que je puisse faire ça maintenant: D
Felix Kling
1
@djKianoosh: Voyez par vous-même . Après l'affectation à module.exports, exportset module.exportsavoir des valeurs différentes, l'affectation à exports.defaultsn'a donc aucun effet (car module.exportsc'est ce qui est exporté). En d'autres termes, c'est exactement la même chose que si vous l'aviez fait module.exports = { ... }.
Felix Kling
1

Vous devez configurer correctement babel dans votre projet pour utiliser l'exportation par défaut et l'exportation const foo

npm install --save-dev @babel/plugin-proposal-export-default-from

puis ajoutez ci-dessous la configration dans .babelrc

"plugins": [ 
       "@babel/plugin-proposal-export-default-from"
      ]
Hassan Azhar Khan
la source
1

Felix Kling a fait une grande comparaison sur ces deux, pour tous ceux qui se demandent comment faire une exportation par défaut aux côtés des exportations nommées avec module.exports dans nodejs

module.exports = new DAO()
module.exports.initDAO = initDAO // append other functions a named export

// now you have
let DAO = require('_/helpers/DAO');
// DAO by default is exported class or function
DAO.initDAO()
moein rahimi
la source
-61

tl; dr en ce moment pour que cela fonctionne, le fichier requis ou importé SlimShadydoit être compilé avec Babel avec 'use strict'.

J'utilise babel-cli6.18.0 dans le projet où j'ai rencontré initialement cette erreur.

Sans les 'use strict'mauvaises nouvelles

var SlimShady = require('./slim-shady');
var marshall = new SlimShady();  // uh, oh...

«utiliser strictement», s'il vous plaît

'use strict'
import SlimShady from './slim-shady'
var marshall = new SlimShady()  // all good in the hood
Marty Chang
la source
13
Cela n'a aucun sens. Chaque source qui utilise des importdéclarations est un module, et celles-ci sont déjà strictes. La différence réelle concerne l'exigence par rapport à l'importation.
Bergi
1
Ce qui est logique, c'est d'utiliser à la importplace de requireet export defaultau lieu de exports.default.
Corey Alix
104
Cela doit être la réponse la plus downvoted que j'ai jamais vue sur stackoverflow
Jimi
4
@Jimi C'est parce que c'est la quatrième réponse la moins bien notée sur l'ensemble du site.
pppery