module.exports против экспорта по умолчанию в Node.js и ES6

318

В чем разница между Node 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
Марти Чанг
источник

Ответы:

402

Вопрос с

  • как модули 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 '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