This question already has an answer here:
-
Can't require() default export value in Babel 6.x
2 answers
I want to import a class inside one file:
"use strict";
import models from "../model";
class Foo {
bar() {
}
}
export default new Foo();
It works when I use import, like:
import Foo from "./foo";
console.log(Foo.bar); // [Function bar]
The problem is that doing it with require gives me undefined:
var Foo = require("./foo");
console.log(Foo.bar); // undefined
and the Foo variable seems to be an empty class. Why is that so? What's going on?
TL;DR
This is because importing with import
is different than require
. When you import a module with the syntax import X from "Y"
, the default export is automatically imported, as the syntax is meant to import the default export by specification. However, when you require
, the default export is not automatically imported like you'd expect. You must add .default
to get the default export with require
like: require("./foo").default
.
Babel Transpilation
Take a look at how Babel transpiles some example code:
export { x }
Becomes:
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.x = x;
This makes sense as x
is just exported normally. Then you would proceed to do:
require("module").x;
To receive x
. Similarly, try the following:
export default new Foo();
That becomes:
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = new Foo();
You'll see that the default export is attached to the exports
object as a property named default
, just as x
was exported with property x
. That means, to get the default export with require
, you'll need to do:
require("module").default;
Now to explain the why it's undefined
. In the line:
var Foo = require("./foo");
Foo
here is actually just an object with a default
property:
{
default: //Whatever was exported as default
}
Thus, trying to do Foo.bar
will yield undefined
because there is no bar
property. You need to access the default
property to access your instance.
You might think this is a little clunky, and other do too. That's why there's a plugin to get rid of the need for the extra .default
. The plugin does module.exports = exports["default"]
to assign the module.exports
from ES5, to exports["default"]
.1
Difference Between import
and require
For import
syntax, Babel does the following transpilation:
import Foo from "./foo";
Foo.bar();
Becomes:
"use strict";
var _foo = require("./foo");
var _foo2 = _interopRequireDefault(_foo);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
_foo2.default.bar();
To break it down line by line, Babel just require
s the module. Then, it calls _interopRequireDefault
on the module. A check is done with obj && obj.__esModule
to make sure that the module actually exports anything and the module is exported with ES2015/ES6 syntax. If it is, then the module is returned as is, or else { default: obj }
is returned. This is to ensure that module.exports = x
in ES5 is treated the same as export default x
in ES2015/ES6. You'll notice that for default import syntax, Babel automatically adds .default
to retrieve the default export.
However, the corresponding code with require
:
var Foo = require("./foo");
Foo.bar();
Is completely valid ES5 code, so nothing is transpiled. Consequently, .default
is never added to Foo
when it is used. Thus, the default export is not retrieved.
Notes
1It should be noted that module.exports
and exports
just refer to the same object, see this answer. module.exports
holds the data which is exported from the module. The reason why module.exports = x
successfully exports x
as default and does not need the extra .default
on require
is because you're assigning the module.exports
to one single thing. Since module.exports
holds the data that's imported, require("module")
imports whatever module.exports
is, which is x
. If module.exports
were 3, then require("module")
would be 3. If module.exports
were an object like { blah: "foo" }
, then require("module")
would be { blah: "foo" }
. By default, module
and module.exports
are objects.
One the other hand, exports.default
exports an object (that refers to the same object as module.exports
) with a default
property that holds the default export. You may want to do exports = x
to export x
as default but since exports
is assigned a reference to module.exports
this will break the reference and exports
will no longer point to module.exports
.