-- Main.shssdhakchina - 26 Jan 2022
Exports & Imports
Export and import directives have several syntax variants.
Export before declarations
We can label any declaration as exported by placing export before it, be it a variable, function or a class.
For instance, here all exports are valid:
// export an array
export let months = ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
// export a constant
export const MODULES_BECAME_STANDARD_YEAR = 2015;
// export a class
export class User {
constructor(name) {
this.name = name;
}
}
%i%No semicolons after export class/function
Please note that export before a class or a function does not make it a function expression. It’s still a function declaration, albeit exported.
Most JavaScript style guides don’t recommend semicolons after function and class declarations.
That’s why there’s no need for a semicolon at the end of export class and export function:
export function sayHi(user) {
alert(`Hello, ${user}!`);
} // no ; at the end
Export apart from declarations
Also, we can put export separately.
Here we first declare, and then export:
// 📁 say.js
function sayHi(user) {
alert(`Hello, ${user}!`);
}
function sayBye(user) {
alert(`Bye, ${user}!`);
}
export {sayHi, sayBye}; // a list of exported variables
…Or, technically we could put export above functions as well.
Import *
Usually, we put a list of what to import in curly braces import {...}, like this:
// 📁 main.js
import {sayHi, sayBye} from './say.js';
sayHi('John'); // Hello, John!
sayBye('John'); // Bye, John!
But if there’s a lot to import, we can import everything as an object using import * as <obj>, for instance:
// 📁 main.js
import * as say from './say.js';
say.sayHi('John');
say.sayBye('John');
At first sight, “import everything” seems such a cool thing, short to write, why should we ever explicitly list what we need to import?
Well, there are few reasons.
- Modern build tools (webpack and others) bundle modules together and optimize them to speedup loading and remove unused stuff.
Let’s say, we added a 3rd-party library say.js to our project with many functions:
// 📁 say.js
export function sayHi() { ... }
export function sayBye() { ... }
export function becomeSilent() { ... }
Now if we only use one of say.js functions in our project:
// 📁 main.js
import {sayHi} from './say.js';
…Then the optimizer will see that and remove the other functions from the bundled code, thus making the build smaller. That is called “tree-shaking”.
- Explicitly listing what to import gives shorter names: sayHi() instead of say.sayHi().
- Explicit list of imports gives better overview of the code structure: what is used and where. It makes code support and refactoring easier.
Import “as”
We can also use as to import under different names.
For instance, let’s import sayHi into the local variable hi for brevity, and import sayBye as bye:
// 📁 main.js
import {sayHi as hi, sayBye as bye} from './say.js';
hi('John'); // Hello, John!
bye('John'); // Bye, John!
Export “as”
The similar syntax exists for export.
Let’s export functions as hi and bye:
// 📁 say.js
...
export {sayHi as hi, sayBye as bye};
Now hi and bye are official names for outsiders, to be used in imports:
// 📁 main.js
import * as say from './say.js';
say.hi('John'); // Hello, John!
say.bye('John'); // Bye, John!
Export default
In practice, there are mainly two kinds of modules.
Modules that contain a library, pack of functions, like say.js above.
Modules that declare a single entity, e.g. a module user.js exports only class User.
Mostly, the second approach is preferred, so that every “thing” resides in its own module.
Naturally, that requires a lot of files, as everything wants its own module, but that’s not a problem at all. Actually, code navigation becomes easier if files are well-named and structured into folders.
Modules provide a special export default (“the default export”) syntax to make the “one thing per module” way look better.
Put export default before the entity to export:
// 📁 user.js
export default class User { // just add "default"
constructor(name) {
this.name = name;
}
}
There may be only one export default per file.
…And then import it without curly braces:
// 📁 main.js
import User from './user.js'; // not {User}, just User
new User('John');
Imports without curly braces look nicer. A common mistake when starting to use modules is to forget curly braces at all. So, remember, import needs curly braces for named exports and doesn’t need them for the default one.