ES6 Export Import Ways


Published: 2018-01-08
Updated: 2018-01-22
Web: http://fritzthecat-blog.blogspot.com/2018/01/es6-export-import-ways.html


This Blog is about the (too) many possibilities that ES6 gives us for exporting and importing fields, functions and classes.

I just read some articles about how we should export and import, and now I'm puzzled and confused. The technical backgrounds about why some export/import pattern is good or bad stay in the dark. Or what would you suppose to be behind a sentence like "You lose some ES6 module benefits such as tree-shaking and faster access to imports" - ? All I can give by now is a quick overview.

General

Both ES6 exports and imports are possible just on ground-level, they can not be inside some function or code-block.

Imports moreover are "hoisted", that means they get resolved before the source-code is executed where they occur.

Export

Exports are some kind of access modifiers. They state which field, function or class are publicly available for importers.

Named Inline Exports

Simply put the export keyword before the field, function or class to make it public.

./modules/io/output.js
/* publics */
export const ERROR = 0
export const SUCCESS = 1

export function display(text, code) {
    message(text, code === ERROR ? "error" : "success")
}

/* privates */
function message(text, elementId) {
    const outputElement = document.getElementById(elementId)
    outputElement.innerHTML += text+"<br>"
}
./main.js
import { display, SUCCESS, ERROR } from "./modules/io/output.js"

display("Hello Success", SUCCESS)
display("Goodbye ERROR", ERROR)

Advantages:

Disadvantages:

Separate Export Clause

The export keyword is used as the opener for a list of names that should be exported.

./modules/io/output.js
/* publics */
const ERROR = 0
const SUCCESS = 1

function display(text, code) {
    message(text, code === ERROR ? "error" : "success")
}

export { ERROR, SUCCESS, display }

/* privates */
function message(text, elementId) {
    const outputElement = document.getElementById(elementId)
    outputElement.innerHTML += text+"<br>"
}

The according import is the same as with named inline exports.

Disadvantages:

Export Renaming

./modules/io/info.js
function displayInfo(text) {
    alert(text)
}

export { displayInfo as info }
./main.js
import { info } from "./modules/io/info.js"

info("Hello Info")

Disadvantages:

One Default Export per Module

You add default after the export keyword. You can combine the default-export with any number of named inline exports.

./modules/io/info.js
export default function displayInfo(text) {
    const outputElement = document.getElementById("info")
    outputElement.innerHTML += text+"<br>"
}
./main.js
import displayInfo from "./modules/io/info.js"

displayInfo("Hello Info")

Advantages:

Disadvantages:

Export Rules of Thumb:

Import

Other than export, which plays the role of an access modifier, import is a dependency statement. The ES6 interpreter searches for imports before executing the source code in a module it loads. When it can't resolve all dependencies, or one has a syntax-error, it doesn't execute the loading module either.

ES6 seems to give three options for resolving imported files:

  1. path relative to the location of the importer, always starting with "./", that's what I used in all examples of this article
  2. absolute path (I wouldn't use that unless you want to bind your library-structure to the file-system of your computer)
  3. module names, has to be configured, not specified how, maybe the best way but not yet ready

Named Import

Here I refer to the exports of output.js module presented at the beginning of this article.

./main.js
import { display, SUCCESS, ERROR } from "./modules/io/output.js"

display("Hello Success", SUCCESS)
display("Goodbye ERROR", ERROR)

Advantages:

Disadvantages:

Wildcard Import onto Module-Object

The star (asterisk, *) is called "wildcard". It denotes all exports.

./main.js
import * as output from "./modules/io/output.js"

output.display("Hello Success!", output.SUCCESS)

This import-statement creates a new object output that contains all exports of the imported module. You can use them by dereferencing the module object, e.g. output.SUCCESS.

The "as modulename" clause is required! You could say modulename is a "namespace".

Advantages:

Disadvantages:

Import Renaming

To resolve ambiguity when importing the same name from different modules, import provides renaming via the "as" keyword.

./main.js
import { display as displayOnConsole } from "./modules/io/console.js"
import { display as displayOnPage } from "./modules/io/page.js"

displayOnConsole("Hello Console!")
displayOnPage("Hello Page!")

You wouldn't need that when importing by wildcard, then each of the functions would be bound to the imported module-object.

Advantages:

Disadvantages:

Import Rules of Thumb:

Resume

This article is not complete. For example I did not discuss anonymous default exports, or re-exporting, because I consider them to be dispensable features.

The question rises whether it makes sense to provide so many different export/import ways in a programming language that claims to become a standard, running in millions of web-browsers. Standards live from being understood by a maximum number of people, thus they need to be unambiguous and simple, a base for communication.

Nevertheless this is a big step forward. Finally JavaScript got dependency management, built into browsers!
Please refer to the many web-pages about ES6 modules for deeper information about how to export and import.





ɔ⃝ Fritz Ritzberger, 2018-01-08