导入与需求与巴贝尔节点

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

"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 

而Foovariables似乎是一个空的类。 为什么? 这是怎么回事?

TL; DR

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

Babel Transpilation

看看巴贝尔如何编译一些示例代码:

 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(); 

您将看到默认导出作为名为default的属性附加到了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属性来访问您的实例。

你可能会觉得这有点笨重,其他的也可以。 这就是为什么有一个插件来摆脱额外的.default的需要。 插件的module.exports = exports["default"]将ES5中的module.exports分配给exports["default"]1

importrequire区别

对于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只require模块。 然后,它调用模块上的_interopRequireDefault 。 使用obj && obj.__esModule进行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代码,所以没有什么是transpiled。 因此,使用.default永远不会添加到Foo 。 因此,默认导出不被检索。


笔记

1应该注意的是module.exportsexports只是指向同一个对象,看到这个答案 。 module.exports保存从模块导出的数据。 之所以module.exports = x成功地将x导出为默认值,并且不需要额外的.default on require ,因为您将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是对象。

另一方面, module.exports用一个保存默认导出的default属性导出一个对象(引用与module.exports相同的对象)。 您可能需要执行exports = x来将x导出为默认值,但是由于exports被分配了对module.exports的引用,因此将会中断引用, exports将不再指向module.exports