module.exports vs. export default w Node.js i ES6

317

Jaka jest różnica między Węzłem module.exportsa ES6 export default? Próbuję dowiedzieć się, dlaczego pojawia się błąd „__ nie jest konstruktorem”, gdy próbuję export defaultw Node.js 6.2.2.

Co działa

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

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

// This works
module.exports = SlimShady

Co nie działa

'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
źródło

Odpowiedzi:

401

Problem dotyczy

  • jak emulowane są moduły ES6 we CommonJS
  • jak importujesz moduł

ES6 do CommonJS

W chwili pisania tego tekstu żadne środowisko nie obsługuje natywnie modułów ES6. Używając ich w Node.js, musisz użyć czegoś takiego jak Babel, aby przekonwertować moduły na CommonJS. Ale jak to dokładnie się dzieje?

Wiele osób uważa module.exports = ...za równoważne export default ...i exports.foo ...równoważne export const foo = .... To nie do końca prawda, a przynajmniej nie tak to robi Babel.

defaultEksport ES6 jest w rzeczywistości również nazywany eksportem, z tą różnicą, że defaultjest to nazwa „zarezerwowana” i istnieje specjalna obsługa składni. Zobaczmy, jak Babel kompiluje nazwy i domyślny eksport:

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

Tutaj widzimy, że domyślny eksport staje się właściwością exportsobiektu, podobnie jak foo.

Zaimportuj moduł

Możemy zaimportować moduł na dwa sposoby: albo używając CommonJS, albo używając importskładni ES6 .

Twój problem: uważam, że robisz coś takiego:

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

oczekiwanie, że barjest przypisana wartość domyślnego eksportu. Ale jak widać w powyższym przykładzie, domyślny eksport jest przypisany do defaultwłaściwości!

Aby więc uzyskać dostęp do domyślnego eksportu, musimy to zrobić

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

Jeśli użyjemy składni modułu ES6, a mianowicie

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

Babel to przekształci

'use strict';

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

var _input2 = _interopRequireDefault(_input);

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

console.log(_input2.default);

Możesz zobaczyć, że każdy dostęp do barjest konwertowany na dostęp .default.

Felix Kling
źródło
Czy nie mamy na to duplikatu?
Bergi
3
@Bergi: Nie szukałem tbh (wstyd mnie :(). Z pewnością istnieją pytania dotyczące tego samego problemu, ale zadawane w inny sposób. Daj mi znać, jeśli znajdziesz coś, co pasuje!
Felix Kling
1
OK, znalezienie ich zajęło trochę czasu, ale możesz teraz użyć nowo nabytych mocy i wybrać jedną z opcji Jak poprawnie użyć ES6 „domyślnie eksportuj” z CommonJS „wymagaj”? i nie może wymagać () domyślnej wartości eksportu w Babel 6.x jako celu dupe :-)
Bergi
1
Jak ironiczne, że mogę to teraz zrobić: D
Felix Kling
1
@djKianoosh: przekonaj się sam . Po przypisania do module.exports, exportsi module.exportsmają różne wartości, tak że zadanie exports.defaultsnie ma wpływu (bo module.exportsto, co dostaje eksportowane). Innymi słowy, jest dokładnie tak samo, jakbyś to zrobił module.exports = { ... }.
Felix Kling
1

Musisz poprawnie skonfigurować Babel w swoim projekcie, aby używać domyślnego eksportu i eksportować const foo

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

następnie dodaj poniżej konfigurację w .babelrc

"plugins": [ 
       "@babel/plugin-proposal-export-default-from"
      ]
Hassan Azhar Khan
źródło
1

Felix Kling zrobił świetne porównanie tych dwóch, dla każdego, kto zastanawia się, jak zrobić domyślny eksport obok nazwanych eksportów z module.exports w 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
źródło
-61

tl; dr teraz, aby to zadziałało, plik wymagający lub importujący SlimShadymusi zostać skompilowany przy użyciu Babel z 'use strict'.

Korzystam z wersji babel-cli6.18.0 w projekcie, w którym początkowo napotkałem ten błąd.

Bez 'use strict'jest zła wiadomość Niedźwiedzie

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

proszę stosować „ścisłe”

'use strict'
import SlimShady from './slim-shady'
var marshall = new SlimShady()  // all good in the hood
Marty Chang
źródło
13
To nie ma sensu. Każde źródło korzystające z importdeklaracji jest modułem, a te już są ścisłe. Rzeczywista różnica polega na wymaganiu kontra importowaniu.
Bergi,
1
Sensowne jest używanie importzamiast requirei export defaultzamiast exports.default.
Corey Alix
104
To musi być najbardziej zaniżona odpowiedź, jaką kiedykolwiek widziałem przy przepełnieniu stosu
Jimi
4
@Jimi To dlatego, że jest czwartą najczęściej ocenianą odpowiedzią w całej witrynie.
pppery