JS Modules are file-based, meaning one module per file.
Modules are singletons, meaning there's always only one instance of the module (and it's state).
Named Exports
export function foo() { ... }
export let bar = 23;
const baz = [1, 2, 3];
export { baz };
// aliased exports
function foo() { ... }
export { foo as bar };
Named exports are actual bindings (kind of like pointers) to the exported thing (variable, function, etc). For example:
// File #1
let awesome = 23;
function update() { awesome = 100; }
export { awesome, update };
// File #2
import { awesome, update } from './file-1';
console.log(awesome); // 23
update();
console.log(awesome); // 100
Even though awesome
holds a primitive value (and primitive values are immutable),
because the awesome
export is a binding to the variable (not to the value), the second output is 100
.
Take-away: named exports are bindings to the variable, not just references to the value.
Default Exports
Only one default export per file
function foo() { ... }
export default foo;
export default function bar() { ... }
function baz() { ... }
export { baz as default };
let me = 23;
export default me;
// NOTE: combining these two lines into one is not valid.
// export default let me = 23; // would result in a syntax error
Standard default exports export a binding, but default is not a legal identifier, so that binding is not actually accessible.
// File #1
let awesome = 23;
function update() { awesome = 100; }
export default { awesome, update };
// File #2
import stuff from './file-1';
console.log(stuff.awesome); // 23
stuff.update();
console.log(stuff.awesome); // 23
Take-away: default exports are bindings to the value, not the variable.
Combining named exports with default exports
When using both named exports and a default export, make sure you are importing what you want (use default import syntax when importing default exports, use named import syntax when importing named exports)
Review: Named Exports / Imports
// File #1
export let awesome = 23;
// File #2
import { awesome } from './file-1';
Review: Default Exports / Import
// File #1
let awesome = 23;
export default awesome;
// File #2
import stuff from './file-1';
console.log(stuff.awesome); // 23
Combining Named and Default Exports / Imports
// File #1
export let awesome = 23;
export function update() { awesome = 100; }
export default { awesome, update };
// File #2
import stuff, { awesome, update } from './file-1'; // stuff is imported with default import syntax
console.log(awesome); // 23
update();
console.log(awesome); // 100
...
console.log(stuff.awesome); // 23
stuff.update();
console.log(stuff.awesome); // 23
NOTE: importing members that are not exported will result in undefined
imports
// File #1
let awesome = 23; // not exported as named export
export function update() { awesome = 100; } // exported as named export
export default { awesome }; // update is not exported as part of the default export object
// File #2
import stuff, { awesome, update } from './file-1';
console.log(awesome); // undefined (`awesome` was not a named export)
update();
console.log(awesome); // undefined (still not a named export)
...
console.log(stuff.awesome); // 23
stuff.update(); // TypeError (update was not part of the default export object (and so is not a function))
Also Noteworthy
Either webpack or babel has been letting some bad syntax linger / accumulate in our react code...
The following import is not syntactically correct (it's only by chance that it's working with our setup)...
// File #1
let awesome = 23;
function update() { awesome = 100; }
export default { awesome, update };
// File #2
import { awesome, update } from './file-1'; // Using named import syntax for default exports!!!
The correct import syntax for a default export would be
// File #2
import foo from './file-1';
Related SO question/answer: http://stackoverflow.com/a/43987935/379512
I've created a ticket (FE-916) to correct this.