module.exports vs експорту за замовчуванням у Node.js та ES6


317

Яка різниця між вузлами module.exportsта ES6 export default? Я намагаюся з'ясувати, чому я отримую помилку "__ не є конструктором", коли намагаюся export defaultв Node.js 6.2.2.

Що працює

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

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

// This works
module.exports = SlimShady

Що не працює

'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

Відповіді:


401

Питання - с

  • як емуляції модулів ES6 у CommonJS
  • як ви імпортуєте модуль

ES6 до CommonJS

На момент написання цього запису жодне середовище не підтримує модулі ES6 в оригінальній формі. При використанні їх у Node.js вам потрібно використовувати щось на зразок Babel для перетворення модулів у CommonJS. Але як саме це відбувається?

Багато людей вважають module.exports = ...рівнозначним export default ...і exports.foo ...рівнозначним export const foo = .... Це не зовсім правда, або, принаймні, не так, як це робить Вавилон.

defaultЕкспорт ES6 насправді також називається експортом, за винятком того, що defaultце "зарезервоване" ім'я, і ​​для нього є спеціальна синтаксична підтримка. Давайте подивимось, як Babel компілює імпорт і експорт за замовчуванням:

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

Тут ми можемо побачити, що експорт за замовчуванням стає властивістю exportsоб’єкта, як і foo.

Імпортуйте модуль

Ми можемо імпортувати модуль двома способами: або за допомогою CommonJS, або за допомогою importсинтаксису ES6 .

Ваше питання: Я вважаю, ви робите щось на кшталт:

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

очікуючи, що barприсвоєно значення експорту за замовчуванням. Але як ми бачимо у наведеному вище прикладі, експорт за замовчуванням призначається defaultвластивості!

Отже, щоб отримати доступ до експорту за замовчуванням, ми насправді повинні зробити

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

Якщо ми використовуємо синтаксис модуля ES6, а саме

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

Вавілон перетворить його на

'use strict';

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

var _input2 = _interopRequireDefault(_input);

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

console.log(_input2.default);

Ви можете бачити, що кожен доступ до barперетворюється на доступ .default.


Хіба у нас немає дубліката для цього?
Бергі

3
@Bergi: Я не шукав tbh (сором мені :(). Звичайно, є питання щодо тієї самої проблеми, але його задали по-іншому. Повідомте мене, якщо ви знайдете щось, що підходить!
Фелікс Клінг

1
Гаразд, знадобилося певний час, щоб знайти їх, але ви можете зараз скористатися вашими нещодавно придбаними повноваженнями та вибрати одне із способів правильно використовувати "експорт за замовчуванням" ES6 із CommonJS "вимагати"? і не можу вимагати () значення експорту за замовчуванням у Babel 6.x в якості цільової мети :-)
Bergi

1
Як іронічно, що я можу це зробити зараз: D
Фелікс Клінг

1
@djKianoosh: Побачте самі . Після присвоєння module.exports, exportsі module.exportsмають різні значення, тому присвоєння exports.defaultsне має ніякого ефекту (бо module.exportsце те , що експортується). Іншими словами, це точно так само, як якщо б ви тільки зробили module.exports = { ... }.
Фелікс Клінг

1

Вам потрібно правильно налаштувати babel у своєму проекті для використання експорту за замовчуванням та експорту const foo

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

потім додайте нижче конфігурацію в .babelrc

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

1

Фелікс Клінг зробив чудове порівняння з цими двома, для тих, хто цікавиться, як зробити експортний дефолт поряд з названим експортом з module.exports в 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 зараз, щоб це працювало, файл, який вимагає чи імпортувати, SlimShadyповинен бути зібраний за допомогою Babel with 'use strict'.

Я використовую babel-cli6.18.0 в проекті, де я спочатку зіткнувся з цією помилкою.

Без 'use strict'- це погані новини

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

"будьте суворі", будь ласка

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

13
Це не має сенсу. Кожне джерело, яке використовує importдекларації, є модулем, а ті вже суворі. Фактична різниця полягає в тому, щоб вимагати відновлення імпорту.
Бергі

1
Що має сенс використовує importзамість requireі export defaultзамість exports.default.
Корі Алікс


104
Це має бути найприхильнішою відповіддю, яку я коли-небудь бачив у stackoverflow
Джімі

4
@Jimi Це тому, що це четверта відповідь на весь сайт.
pppery
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.