导入与在节点中使用Babel的要求

use*_*999 4 javascript node.js ecmascript-6 babeljs

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

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

它在我使用import时有效,例如:

import Foo from "./foo";
console.log(Foo.bar); // [Function bar]
Run Code Online (Sandbox Code Playgroud)

问题是,使用require执行它会给我未定义:

var Foo = require("./foo");
console.log(Foo.bar); // undefined
Run Code Online (Sandbox Code Playgroud)

而Foo变量似乎是一个空类.为什么会这样?这是怎么回事?

Li3*_*357 9

TL; DR

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

Babel Transpilation

看看Babel如何转换一些示例代码:

export { x }
Run Code Online (Sandbox Code Playgroud)

变为:

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.x = x;
Run Code Online (Sandbox Code Playgroud)

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

require("module").x;
Run Code Online (Sandbox Code Playgroud)

收到x.同样,请尝试以下操作:

export default new Foo();
Run Code Online (Sandbox Code Playgroud)

那就变成:

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = new Foo();
Run Code Online (Sandbox Code Playgroud)

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

require("module").default;
Run Code Online (Sandbox Code Playgroud)

现在来解释一下它的原因undefined.在线:

var Foo = require("./foo");
Run Code Online (Sandbox Code Playgroud)

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

{
    default: //Whatever was exported as default
}
Run Code Online (Sandbox Code Playgroud)

因此,尝试做Foo.bar会产生undefined因为没有bar财产.您需要访问该default属性才能访问您的实例.

你可能会认为这有点笨重,其他人也这样做.这就是为什么有一个插件来摆脱额外的需要.default.该插件module.exports = exports["default"]用于将module.exportsES5 分配给exports["default"].1

import和之间的区别require

对于import语法,Babel执行以下转换:

import Foo from "./foo";
Foo.bar();
Run Code Online (Sandbox Code Playgroud)

变为:

"use strict";

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

var _foo2 = _interopRequireDefault(_foo);

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

_foo2.default.bar();
Run Code Online (Sandbox Code Playgroud)

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

但是,相应的代码有require:

var Foo = require("./foo");
Foo.bar();
Run Code Online (Sandbox Code Playgroud)

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


笔记

1应该注意的是module.exports,exports只是引用同一个对象,请参阅此答案.module.exports保存从模块导出的数据.为什么原因module.exports = x成功出口x为默认值,不需要额外的.defaultrequire是因为你分配module.exports到一个单一的东西.自module.exports认为的输入型的数据,require("module")进口无论module.exports是,这是x.如果module.exports是3,那么require("module")将是3.如果module.exports是一个对象{ blah: "foo" },那么require("module")就是{ blah: "foo" }.默认情况下,modulemodule.exports是对象.

另一方面,使用保存默认导出的属性导出exports.default对象(与同一对象引用module.exports)default.您可能希望exports = x导出x为默认值,但由于exports分配了module.exports对此的引用exports将导致引用中断,并且将不再指向module.exports.