module.exports vs. export default in Node.js ed ES6


318

Qual è la differenza tra Node module.exportse ES6 export default? Sto cercando di capire perché ottengo l'errore "__ non è un costruttore" quando provo export defaultin Node.js 6.2.2.

Cosa funziona

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

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

// This works
module.exports = SlimShady

Cosa non funziona

'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

Risposte:


402

Il problema è con

  • come vengono emulati i moduli ES6 in CommonJS
  • come importare il modulo

ES6 a CommonJS

Al momento della stesura di questo documento, nessun ambiente supporta i moduli ES6 in modo nativo. Quando li usi in Node.js devi usare qualcosa come Babel per convertire i moduli in CommonJS. Ma come succede esattamente?

Molte persone considerano module.exports = ...equivalenti export default ...e exports.foo ...equivalenti export const foo = .... Questo non è del tutto vero, o almeno non come Babel lo fa.

Le defaultesportazioni ES6 sono in realtà anche denominate esportazioni, tranne per il fatto che defaultè un nome "riservato" e per esso esiste uno speciale supporto di sintassi. Diamo un'occhiata a come Babel compila le esportazioni nominate e predefinite:

// 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; 

Qui possiamo vedere che l'esportazione predefinita diventa una proprietà exportssull'oggetto, proprio come foo.

Importa il modulo

È possibile importare il modulo in due modi: utilizzando CommonJS o utilizzando la importsintassi ES6 .

Il tuo problema: credo che stai facendo qualcosa del tipo:

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

prevedendo che barvenga assegnato il valore dell'esportazione predefinita. Ma come possiamo vedere nell'esempio sopra, l'esportazione predefinita è assegnata alla defaultproprietà!

Quindi, per accedere all'esportazione predefinita, dobbiamo effettivamente farlo

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

Se utilizziamo la sintassi del modulo ES6, vale a dire

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

Babele lo trasformerà in

'use strict';

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

var _input2 = _interopRequireDefault(_input);

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

console.log(_input2.default);

Puoi vedere che ogni accesso a barviene convertito in accesso .default.


Non abbiamo un duplicato per questo?
Bergi,

3
@Bergi: non ho cercato tbh (peccato per me :(). Ci sono sicuramente domande sullo stesso problema, ma fatte in modo diverso. Fammi sapere se trovi qualcosa che si adatta!
Felix Kling

1
OK, ci è voluto del tempo per trovarli, ma ora puoi usare i tuoi poteri appena acquisiti e scegliere uno dei Come usare correttamente ES6 "default di esportazione" con CommonJS "richiede"? e non posso richiedere () il valore di esportazione predefinito in Babel 6.x come target dupe :-)
Bergi,

1
È ironico che ora possa farlo: D
Felix Kling,

1
@djKianoosh: guarda tu stesso . Dopo l'assegnazione a module.exports, exportse module.exportshanno valori diversi, quindi l'assegnazione a exports.defaultsnon ha alcun effetto (perché module.exportsè ciò che viene esportato). In altre parole, è esattamente lo stesso che se lo avessi fatto solo tu module.exports = { ... }.
Felix Kling

1

Devi configurare babel correttamente nel tuo progetto per usare export default e export const foo

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

quindi aggiungi la configrazione di seguito in .babelrc

"plugins": [ 
       "@babel/plugin-proposal-export-default-from"
      ]

1

Felix Kling ha fatto un ottimo confronto su quei due, per chiunque si chiedesse come fare un default di esportazione insieme a export con nome module.exports in 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()

-61

tl; dr adesso perché questo funzioni, il file che richiede o importa SlimShadydeve essere compilato usando Babel con 'use strict'.

Sto usando babel-cli6.18.0 nel progetto in cui inizialmente ho riscontrato questo errore.

Without 'use strict'is Bad News Bears

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

'usa rigoroso', per favore

'use strict'
import SlimShady from './slim-shady'
var marshall = new SlimShady()  // all good in the hood

13
Questo non ha senso. Ogni fonte che utilizza importdichiarazioni è un modulo e quelli già sono rigorosi. La differenza reale riguarda la necessità di importare.
Bergi,

1
Ciò che ha senso è usare importinvece di requiree export defaultinvece di exports.default.
Corey Alix,


104
Questa deve essere la risposta più sottovalutata che io abbia mai visto su StackOverflow
Jimi,

4
@Jimi Questo perché è la quarta risposta più votata all'intero sito.
pepery
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.