I've just started working on a small node project that will interface with a MongoDB. However, I cannot seem to get the relevant node modules to import correctly, even though I have installed them correctly via npm
.
For example, the following code throws an error, telling me that "express has no default export":
import express from "express";
However, this code works:
const express = require("express");
So my question is, what is the difference in how the import and variable/require methods function? I'd like to fix whatever is plaguing my imports on the project, as it seems likely to cause additional problems down the road.
express
will be of type any
. You could include the definitions from here npmjs.com/package/@types/express
import x = require('x')
is not the same as var x = require('x')
.
This simple diagram will help you understand the difference between require
and import
.
https://i.stack.imgur.com/5WgFJ.png
Apart from that,
You can't selectively load only the pieces you need with require
but with import
, you can selectively load only the pieces you need, which can save memory.
Loading is synchronous(step by step) for require
on the other hand import
can be asynchronous(without waiting for previous import) so it can perform a little better than require
.
The major difference between require
and import
, is that require
will automatically scan node_modules
to find modules, but import
, which comes from ES6, won't.
Most people use babel to compile import
and export
, which makes import
act the same as require
.
The future version of Node.js might support import
itself (actually, the experimental version already does), and judging by Node.js' notes, import
won't support node_modules
, it base on ES6, and must specify the path of the module.
So I would suggest you not use import
with babel, but this feature is not yet confirmed, it might support node_modules
in the future, who would know?
For reference, below is an example of how babel can convert ES6's import
syntax to CommonJS's require
syntax.
Say the fileapp_es6.js
contains this import:
import format from 'date-fns/format';
This is a directive to import the format function from the node package date-fns.
The related package.json
file could contain something like this:
"scripts": {
"start": "node app.js",
"build-server-file": "babel app_es6.js --out-file app.js",
"webpack": "webpack"
}
The related .babelrc
file could be something like this:
{
"presets": [
[
"env",
{
"targets":
{
"node": "current"
}
}
]
]
}
This build-server-file
script defined in the package.json
file is a directive for babel to parse the app_es6.js
file and output the file app.js
.
After running the build-server-file
script, if you open app.js
and look for the date-fns
import, you will see it has been converted into this:
var _format = require("date-fns/format");
var _format2 = _interopRequireDefault(_format);
Most of that file is gobbledygook to most humans, however computers understand it.
Also for reference, as an example of how a module can be created and imported into your project, if you install date-fns
and then open node_modules/date-fns/get_year/index.js
you can see it contains:
var parse = require('../parse/index.js')
function getYear (dirtyDate) {
var date = parse(dirtyDate)
var year = date.getFullYear()
return year
}
module.exports = getYear
Using the babel process above, your app_es6.js
file could then contain:
import getYear from 'date-fns/get_year';
// Which year is 2 July 2014?
var result = getYear(new Date(2014, 6, 2))
//=> 2014
And babel would convert the imports to:
var _get_year = require("date-fns/get_year");
var _get_year2 = _interopRequireDefault(_get_year);
And handle all references to the function accordingly.
require
anyway
import won't support node_modules
What did you mean by that?
import
and require
both scan node_modules
for the package specified by the statement. require
loads anything assigned to module.exports
in the package to the variable it's assigned to, or global scope if no left hand is declared. However, import
will only load an es6 default export by name, unless all are assigned to an alias: import * as X from 'pkg'
. You can import es6 packages with no default using object destructuring too: import { X } from 'pkg'
. It'll work the same as require
if you import the entire package, including all exports, to global scope import 'package'
.
Let me give an example for Including express module with require & import
-require
var express = require('express');
-import
import * as express from 'express';
So after using any of the above statement we will have a variable called as 'express' with us. Now we can define 'app' variable as,
var app = express();
So we use 'require' with 'CommonJS' and 'import' with 'ES6'.
For more info on 'require' & 'import', read through below links.
require - Requiring modules in Node.js: Everything you need to know
import - An Update on ES6 Modules in Node.js
import
statement, and was confused by the express has no default export error. This answer provides the solution. Modules with multiple (and even single) exports which do not define a default export
will need to have all exports assigned to a named variable, as the answer explains: import * as whatever from 'package';
node_modules
(the entrypoint will be listed under its package.json
main
key). Something like module.export = whatever
means you likely have to import it as import * as whatever from 'package';
.
I will make it simple,
Import and Export are ES6 features(Next gen JS).
Require is old school method of importing code from other files
Major difference is in require, entire JS file is called or included. Even if you don't need some part of it.
var myObject = require('./otherFile.js'); //This JS file will be included fully.
Whereas in import you can extract only objects/functions/variables which are required.
import { getDate }from './utils.js';
//Here I am only pulling getDate method from the file instead of importing full file
Another major difference is you can use require
anywhere in the program where as import
should always be at the top of file
Edit: In Latest node versions you can use destructuring. It will look like this
const { getDate } = require('./date.js');
require
, e.g. const { getDate } = require('./utils.js');
require
anywhere in the program while import
only at the top of the file is disguising important details. When you use require
scoped to a function (or block scope somewhere in the application code) rather than scoped to the module/file this has an equivalent with ES modules (aka import
syntax). It's an async operation though and this "dynamic importing" needs a .then()
or an await
keyword to be used.
require
does not "call" or "include" a file, it loads and executes a module. And of course it evaluates the whole code of the module, not just some part - exactly like import
does!
new ES6:
'import' should be used with 'export' key words to share variables/arrays/objects between js files:
export default myObject;
//....in another file
import myObject from './otherFile.js';
old skool:
'require' should be used with 'module.exports'
module.exports = myObject;
//....in another file
var myObject = require('./otherFile.js');
There's a big difference between this:
import express from "express";
and this:
import * as express from "express";
the correct translation from CommonJS to ES6 of
const express = require("express");
is the second import.
Basically, that's because in the first import you are looking for an export in module express
named express
. The second one you are importing the whole express module with name express
.
Success story sharing
module.exports
when the module initialization code finishes running. This difference alone creates compatibility headaches in trying to make a single module work for both ESM and CommonJS.