Node.js和ES6中的module.exports与export default的区别

517

Node的module.exports和ES6的export default有什么区别?我试图弄清楚为什么在尝试在Node.js 6.2.2中使用export default时会出现“__不是构造函数”的错误。

有效的方法

'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

我仍然收到“SlimShady不是构造函数”的错误,你如何将一个类导出为默认值? - brauliobo
3个回答

608

问题出在:

  • 如何在 CommonJS 中模拟 ES6 模块
  • 如何导入模块

ES6 转 CommonJS

截至本文编写时,尚未有任何环境原生支持 ES6 模块。在 Node.js 中使用它们时,您需要使用类似 Babel 的工具将模块转换为 CommonJS 格式。但这是如何实现的呢?

许多人认为module.exports = ... 等同于 export default ...exports.foo ... 等同于 export const foo = ...。但事实并非如此,至少Babel不是这样做的。

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

3
@Bergi: 我实话实说并没有搜索(我感到惭愧 :( )。肯定有关于同样问题的问题,但是用不同的方式提出。如果您找到了合适的内容,请告诉我! - Felix Kling
1
好的,花了一些时间才找到这些内容,但现在你可以使用你新获得的技能,选择其中一个作为重复目标:如何正确地使用ES6“export default”和CommonJS“require”?无法在Babel 6.x中要求默认导出值。 :-) - Bergi
1
多么讽刺,我现在可以做到这个 :D - Felix Kling
1
@djKianoosh: 自己看看吧。 在分配给“module.exports”之后,“exports”和“module.exports”具有不同的值,因此对“exports.defaults”的分配没有影响(因为导出的是“module.exports”)。 换句话说,它与只执行“module.exports = { ... }”完全相同。 - Felix Kling
2
我们如何导出默认值和命名值,以便这两个值都可以在客户端代码中使用:ES6 -> import library, { a, b, c } from "library";commonJS -> const library = require("library"); const { a, b, c } = require("library")?就像在使用React时,当我们使用ES6时,我们可以这样做:import React, { useEffect, useState } from "react";,而在使用commonJS时,我们可以这样做:const React = require("react"); const { useEffect, useState } = require("react");...我们如何在编写自己的库时实现相同的效果呢?谢谢! - tonix
显示剩余4条评论

44

Felix Kling对这两个问题进行了很好的比较,对于任何想知道如何在Node.js中同时使用导出默认值和命名导出的人都有帮助。

module.exports = new DAO()
module.exports.initDAO = initDAO // append other functions as named export

// now you have
let DAO = require('_/helpers/DAO');
// DAO by default is exported class or function
DAO.initDAO()

所以假设initDAO需要对象DAO。我必须导入当前文件本身吗?还是可以调用类似于this.DAO()的东西? - Paul
@Paul 由于 initDAODAO 实例本身上,因此您可以使用 this 来引用 DAO 对象。 - PoolloverNathan

1
你需要在项目中正确配置Babel,才能使用export default和export const foo。
npm install --save-dev @babel/plugin-proposal-export-default-from

然后在 .babelrc 文件中添加以下配置。
"plugins": [ 
       "@babel/plugin-proposal-export-default-from"
      ]

3
我相信这个插件只是用来支持语法export someName from './someModules.js'; - Brett Zamir

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接