【问题标题】:Importing vs Requiring with Babel in Node [duplicate]在节点中使用 Babel 导入与要求 [重复]
【发布时间】:2017-07-17 11:20:32
【问题描述】:

我想在一个文件中导入一个类:

"use strict";
import models from "../model";
class Foo {
    bar() {
    }
}
export default new Foo();

当我使用导入时它可以工作,例如:

import Foo from "./foo";
console.log(Foo.bar); // [Function bar]

问题是用 require 做这件事给了我未定义:

var Foo = require("./foo");
console.log(Foo.bar); // undefined

Foo 变量似乎是一个空类。为什么呢?怎么回事?

【问题讨论】:

  • 你是在使用 Babel 转译这个对吗?
  • 是的,没错。
  • 使用 ES6 模块时可以去掉"use strict"
  • 永远不要导出类的单例实例。要么导出整个类,要么将单例模块创建为对象文字。

标签: javascript node.js ecmascript-6 babeljs


【解决方案1】:

TL;DR

这是因为使用import 导入不同于require。当您使用语法import X from "Y" 导入模块时,会自动导入默认导出,因为该语法旨在按规范导入默认导出。但是,当您require 时,默认导出不会像您期望的那样自动导入。您必须添加.default 才能使用require 获得默认导出,例如:require("./foo").default。

Babel 转译

看一下Babel transpiles的一些示例代码:

export { x }

变成:

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.x = x;

这是有道理的,因为 x 只是正常导出。然后你会继续做:

require("module").x;

接收x。同样,尝试以下操作:

export default new Foo();

这就变成了:

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = new Foo();

您会看到默认导出作为名为@9​​87654339@ 的属性附加到exports 对象,就像x 使用属性x 导出一样。这意味着,要使用require 获得默认导出,您需要执行以下操作:

require("module").default;

现在解释为什么它是undefined。行内:

var Foo = require("./foo");

Foo 这里实际上只是一个带有default 属性的对象:

{
    default: //Whatever was exported as default
}

因此,尝试执行Foo.bar 将产生undefined,因为没有bar 属性。您需要访问 default 属性才能访问您的实例。

您可能会认为这有点笨拙,其他人也一样。这就是为什么there's a plugin 摆脱对额外.default 的需要。该插件使用module.exports = exports["default"] 将ES5 中的module.exports 分配给exports["default"]。1

import 和 require 之间的区别

对于import 语法,Babel 进行以下转译:

import Foo from "./foo";
Foo.bar();

变成:

"use strict";

var _foo = require("./foo");

var _foo2 = _interopRequireDefault(_foo);

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

_foo2.default.bar();

为了逐行分解,Babel 只需 requires 模块。然后,它在模块上调用_interopRequireDefault。使用obj && obj.__esModule 进行检查以确保模块实际导出任何内容并且模块以 ES2015/ES6 语法导出。如果是,则模块按原样返回,否则返回{ default: obj }。这是为了确保 ES5 中的 module.exports = x 被视为与 ES2015/ES6 中的 export default x 相同。你会注意到对于默认导入语法,Babel 会自动添加.default 来检索默认导出。

但是对应的代码跟require:

var Foo = require("./foo");
Foo.bar();

是完全有效的 ES5 代码,所以没有任何东西被转译。因此,.default 在使用时永远不会添加到Foo。因此,不会检索默认导出。


注意事项

1需要注意的是module.exports和exports只是指同一个对象,见this answer。 module.exports 保存从模块导出的数据。 module.exports = x 成功导出x 作为默认值并且不需要require 上的额外.default 的原因是因为您将module.exports 分配给一件事。因为module.exports 保存了导入的数据,所以require("module") 导入module.exports 的任何内容,即x。如果module.exports 是3,那么require("module") 就是3。如果module.exports 是像{ blah: "foo" } 这样的对象,那么require("module") 就是{ blah: "foo" }。默认情况下,module 和 module.exports 是对象。

另一方面,exports.default 导出一个对象(与module.exports 引用相同的对象),该对象的default 属性包含默认导出。您可能希望使用exports = x 将x 导出为默认值,但由于exports 被分配了对module.exports 的引用,这将破坏引用并且exports 将不再指向module.exports。

【讨论】:

    猜你喜欢
    • 2021-12-31
    • 2016-06-07
    • 2020-11-19
    • 2020-06-27
    • 2016-04-12
    • 2019-11-20
    • 2015-09-13
    • 2016-06-12
    相关资源
    最近更新 更多