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

В чем разница между 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

Ответ 1

Проблема с

  • как модули ES6 эмулируются в CommonJS
  • как вы импортируете модуль

ES6 to CommonJS

На момент написания этой статьи никакая среда не поддерживает модули ES6 изначально. При использовании их в Node.js вам нужно использовать что-то вроде Babel для преобразования модулей в CommonJS. Но как именно это происходит?

Многие считают module.exports = ... эквивалентными export default ... и exports.foo ... эквивалентными export const foo = .... Это не совсем верно, хотя, по крайней мере, не так, как это делает Вавилон.

ES6 default экспорт фактически также называется экспортом, за исключением того, что 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, либо с использованием синтаксиса ES6 import.

Ваша проблема: Я считаю, что вы делаете что-то вроде:

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.

Ответ 2

tl; dr прямо сейчас, чтобы это работало, файл, который требуется или импортирующий SlimShady, должен быть скомпилирован с помощью Babel с 'use strict'.

Я использую babel-cli 6.18.0 в проекте, где я впервые столкнулся с этой ошибкой.

Без 'use strict' - плохие новости >

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

'use strict', пожалуйста

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