module.exports vs. padrão de exportação no Node.js e ES6


317

Qual é a diferença entre os module.exportsnós e os ES6 export default? Estou tentando descobrir por que recebo o erro "__ não é um construtor" quando tento export defaultno Node.js. 6.2.2.

O que funciona

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

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

// This works
module.exports = SlimShady

O que não funciona

'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

Respostas:


402

A questão é com

  • como os módulos ES6 são emulados no CommonJS
  • como você importa o módulo

ES6 para CommonJS

No momento em que escrevemos isso, nenhum ambiente suporta módulos ES6 nativamente. Ao usá-los no Node.js, você precisa usar algo como Babel para converter os módulos em CommonJS. Mas como exatamente isso acontece?

Muitas pessoas consideram module.exports = ...ser equivalente export default ...e exports.foo ...equivalente export const foo = .... Isso não é bem verdade, porém, ou pelo menos não como Babel faz.

defaultNa verdade, as exportações do ES6 também são denominadas exportações, exceto que defaulté um nome "reservado" e há suporte especial à sintaxe. Vamos ver como o Babel compila exportações nomeadas e padrão:

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

Aqui podemos ver que a exportação padrão se torna uma propriedade no exportsobjeto, assim como foo.

Importar o módulo

Podemos importar o módulo de duas maneiras: Usando CommonJS ou usando a importsintaxe ES6 .

Seu problema: acredito que você está fazendo algo como:

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

esperando que barseja atribuído o valor da exportação padrão. Mas, como podemos ver no exemplo acima, a exportação padrão é atribuída à defaultpropriedade!

Portanto, para acessar a exportação padrão, precisamos fazer

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

Se usarmos a sintaxe do módulo ES6, a saber

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

Babel irá transformá-lo em

'use strict';

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

var _input2 = _interopRequireDefault(_input);

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

console.log(_input2.default);

Você pode ver que todo acesso a baré convertido em acesso .default.


Não temos uma duplicata para isso?
Bergi 27/10/16

3
@ Bergi: Eu não pesquisei tbh (vergonha para mim :(). Certamente há perguntas sobre o mesmo problema, mas são feitas de uma maneira diferente. Deixe-me saber se você encontrar algo que se encaixe!
Felix Kling

1
OK, demorou algum tempo para encontrá-los, mas agora você pode usar seus poderes recém-adquiridos e escolher uma das opções de Como usar corretamente o "padrão de exportação" do ES6 com o "exigir" do CommonJS? e não pode exigir () valor padrão de exportação em Babel 6.x como um alvo dupe :-)
Bergi

1
Como é irônico que eu possa fazer isso agora: D
Felix Kling

1
@djKianoosh: Veja você mesmo . Após a atribuição de module.exports, exportse module.exportstêm valores diferentes, de modo que a atribuição para exports.defaultsnão tem efeito (porque module.exportsé o que é exportado). Em outras palavras, é exatamente o mesmo que você fez module.exports = { ... }.
Felix Kling 31/03

1

Você precisa configurar o babel corretamente no seu projeto para usar o padrão de exportação e o const foo de exportação

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

adicione abaixo a configração em .babelrc

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

1

Felix Kling fez uma ótima comparação entre os dois, para quem quer saber como fazer um padrão de exportação ao lado de exportações nomeadas com module.exports em 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

Agora, para que isso funcione, o arquivo que está exigindo ou importando SlimShadydeve ser compilado usando Babel com 'use strict'.

Estou usando o babel-cli6.18.0 no projeto em que encontrei esse erro inicialmente.

Sem 'use strict'são más notícias

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

'use strict', por favor

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

13
Isso não faz sentido. Toda fonte que usa importdeclarações é um módulo, e elas já são rigorosas. A diferença real é sobre exigir vs importar.
Bergi 27/10/16

1
O que faz sentido é usar em importvez de requiree em export defaultvez de exports.default.
Corey Alix


104
Isso tem que ser a resposta mais downvoted que eu já vi em stackoverflow
Jimi

4
@Jimi Isso é porque é a quarta resposta com menos votos em todo o site.
pppery 16/09/19
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.