I am trying to determine if there are any big differences between these two, other than being able to import with export default by just doing:
import myItem from 'myItem';
And using export const I can do:
import { myItem } from 'myItem';
Are there any differences and/or use cases other than this?
It's a named export vs a default export. export const is a named export that exports a const declaration or declarations.
To emphasize: what matters here is the export keyword as const is used to declare a const declaration or declarations. export may also be applied to other declarations such as class or function declarations.
Default Export (export default)
You can have one default export per file. When you import you have to specify a name and import like so:
import MyDefaultExport from "./MyFileWithADefaultExport";
You can give this any name you like.
Named Export (export)
With named exports, you can have multiple named exports per file. Then import the specific exports you want surrounded in braces:
// ex. importing multiple exports:
import { MyClass, MyOtherClass } from "./MyClass";
// ex. giving a named import a different name by using "as":
import { MyClass2 as MyClass2Alias } from "./MyClass2";
// use MyClass, MyOtherClass, and MyClass2Alias here
Or it's possible to use a default along with named imports in the same statement:
import MyDefaultExport, { MyClass, MyOtherClass} from "./MyClass";
Namespace Import
It's also possible to import everything from the file on an object:
import * as MyClasses from "./MyClass";
// use MyClasses.MyClass, MyClasses.MyOtherClass and MyClasses.default here
Notes
The syntax favours default exports as slightly more concise because their use case is more common (See the discussion here).
A default export is actually a named export with the name default so you are able to import it with a named import:
import { default as MyDefaultExport } from "./MyFileWithADefaultExport";
export default affects the syntax when importing the exported "thing", when allowing to import, whatever has been exported, by choosing the name in the import itself, no matter what was the name when it was exported, simply because it's marked as the "default".
A useful use case, which I like (and use), is allowing to export an anonymous function without explicitly having to name it, and only when that function is imported, it must be given a name:
Example:
Export 2 functions, one is default:
export function divide( x ){
return x / 2;
}
// only one 'default' function may be exported and the rest (above) must be named
export default function( x ){ // <---- declared as a default function
return x * x;
}
Import the above functions. Making up a name for the default one:
// The default function should be the first to import (and named whatever)
import square, {divide} from './module_1.js'; // I named the default "square"
console.log( square(2), divide(2) ); // 4, 1
When the {} syntax is used to import a function (or variable) it means that whatever is imported was already named when exported, so one must import it by the exact same name, or else the import wouldn't work.
Erroneous Examples:
The default function must be first to import
import {divide}, square from './module_1.js
divide_1 was not exported in module_1.js, thus nothing will be imported
import {divide_1} from './module_1.js
square was not exported in module_1.js, because {} tells the engine to explicitly search for named exports only.
import {square} from './module_1.js
More important difference is: export default exports value, while export const/export var/export let exports reference(or been called live binding). Try below code in nodejs(using version 13 or above to enable es module by default):
// a.mjs
export let x = 5;
// or
// let x = 5;
// export { x }
setInterval(() => {
x++;
}, 1000);
export default x;
// index.mjs
import y, { x } from './1.mjs';
setInterval(() => {
console.log(y, x);
}, 1000);
# install node 13 or above
node ./index.mjs
And we should get below output:
6 5
7 5
8 5
...
...
Why we need this difference
Most probably, export default is used for compatibility of commonjs module.exports.
How to achieve this with bundler(rollup, webpack)
For above code, we use rollup to bundle.
rollup ./index.mjs --dir build
And the build output:
// build/index.js
let x = 5;
// or
// let x = 5;
// export { x }
setInterval(() => {
x++;
}, 1000);
var y = x;
setInterval(() => {
console.log(y, x);
}, 1000);
Please pay attention to var y = x statement, which is the default.
webpack has similar build output. When large scale of modules are added to build, concatenateing text is not sustainable, and bundlers will use Object.defineProperty to achieve binding(or called harmony exports in webpack). Please find detail in below code:
main.js
...
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
...
// 1.js
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[1],[
/* 0 */,
/* 1 */
/***/ (function(__webpack_module__, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "x", function() { return x; });
let x = 5;
// or
// let x = 5;
// export { x }
setInterval(() => {
x++;
}, 1000);
/* harmony default export */ __webpack_exports__["default"] = (x);
/***/ })
]]);
Please find the difference behavior between /* harmony export (binding) */ and /* harmony default export */.
ES Module native implementation
es-modules-a-cartoon-deep-dive by Mozilla told why, what and how about es module.
Minor note: Please consider that when you import from a default export, the naming is completely independent. This actually has an impact on refactorings.
Let's say you have a class Foo like this with a corresponding import:
export default class Foo { }
// The name 'Foo' could be anything, since it's just an
// Identifier for the default export
import Foo from './Foo'
Now if you refactor your Foo class to be Bar and also rename the file, most IDEs will NOT touch your import. So you will end up with this:
export default class Bar { }
// The name 'Foo' could be anything, since it's just an
// Identifier for the default export.
import Foo from './Bar'
Especially in TypeScript, I really appreciate named exports and the more reliable refactoring. The difference is just the lack of the default keyword and the curly braces. This btw also prevents you from making a typo in your import since you have type checking now.
export class Foo { }
//'Foo' needs to be the class name. The import will be refactored
//in case of a rename!
import { Foo } from './Foo'
From the documentation:
Named exports are useful to export several values. During the import, one will be able to use the same name to refer to the corresponding value.
Concerning the default export, there is only a single default export per module. A default export can be a function, a class, an object or anything else. This value is to be considered as the "main" exported value since it will be the simplest to import.
When you put default, its called default export. You can only have one default export per file and you can import it in another file with any name you want. When you don't put default, its called named export, you have to import it in another file using the same name with curly braces inside it.
I have a constant library that has many files such as the following structure:
#1 index.ts
#2 src/
contact.ts
const first = [];
const second = [];
const third = [];
location.ts
const first = '';
const second = '';
const third = '';
Is it possible to import and export all of the constants from each file into index.ts such that I can import into a project such as the following:
// Imports: Constants
import { constants } from '#jeff/constants-library';
console.log(constants.contact.first);
console.log(constants.contact.second);
console.log(constants.contact.third);
What is the fastest/most efficient way to dynamically export the constants from my library files so I can import the into my projects?
In your files like contact and location, you'll need to mark the const values you want as exports. Modules are designed for encapsulation, so dynamic export isn't really an option here. However, it's just a matter of adding the keyword export:
export const first = [];
After that, you can create a constants-library.ts or constants-library/index.ts which automatically exports and imports:
import * as foo from './foo';
import * as bar from './bar';
export const constants = { foo, bar };
At this point, assuming your #jeff path is set up, your code should work as expected:
import { constants } from '#jeff/constants-library';
console.log(constants.contact.first);
Note that this will include all of the exports in your files, since TypeScript can't tell which constants you want or didn't want--it only knows which exports you've listed. As an alternative, rather than exporting all of your consts individually, you could bundle them into a single export using object shorthand.
// in contact.ts and location.ts
export constants = { first, second, third };
// in constant-library.ts or constant-library/index.ts
import { constants as contact } from './contact';
import { constants as location } from './location';
export constants = { contact, location };
I found a lot of articles about circular dependencies and that it indicates the design/architecture flaws in the project. In most of the cases it can be fixed easily by slightly refactoring your classes.
In this example I'm trying to create a hub for dependencies. This is a library of classes that can be imported by different projects. The goal is to have getters in the main class that will return an instance of the dependency. This instance has to be a singleton and it's stored in the main class' dependencies that next time if someone calls a getter - it will return the same instance of that class.
Here is how code looks like:
// deps
import { DepA } from './dep-a';
import { DepB } from './dep-b';
const depsKey = '__MY_DEPS__';
class Deps {
dependencies = {};
get depA() {
return this.getDependency('depA', DepA);
}
get depB() {
return this.getDependency('depB', DepB);
}
bind(key, value) {
this.dependencies[key] = value;
}
getDependency(key, serviceClass) {
let service = this.dependencies[key];
if (!service && !!serviceClass) {
// if instance is not created yet, we instantiate the class and put instance in the dependencies
service = new serviceClass();
this.bind(key, service);
}
return service;
}
}
export const deps = (() => {
window[depsKey] = window[depsKey] || new Deps();
return window[depsKey];
})();
// dep-a
import { deps } from './deps';
export class DepA {
methodA() {
console.log(deps.depB);
}
}
// dep-b
import { deps } from './deps';
export class DepB {
methodB() {
console.log(deps.depA);
}
}
As you can see - this creates a circular dependency problem, since class Deps uses classes DepA and DepB in its getters to create an instance of those classes if it doesn't exist. And classes DepA and DepB use an instance of Deps to retrieve each other via its getters.
I hope this explanation is not very cumbersome.
Can anybody suggest the changes I need to make to get rid of the circular dependency here but to keep an idea of accessing singletons via Deps class (deps instance)?
My recommendation would be this:
a-singleton.js
import { DepA } from './dep-a';
let singleton;
export function getA() {
if (!singleton) {
singleton = new DepA();
}
return singleton;
}
b-singleton.js
import { DepB } from './dep-b';
let singleton;
export function getB() {
if (!singleton) {
singleton = new DepB();
}
return singleton;
}
then wherever you need these singletons, you can import the file and call the function to get the singleton.
Depending on what these classes do and how/when they reference eachother, you may also be able to directly do export default new DepA(), but depending on how they reference eachother, and how much they need to do at instantiation time, the lazy approach I've shown here can be better or necessary.
If I have the following code
class x {
...
}
export CreateFragmentContainer(x,graphqlQuery)
Will the bundle size of my project if I do an another export of class x without the fragmentContainer such as the below?
export class x {
...
}
export CreateFragmentContainer(x,graphqlQuery)
Also, will the answer to this question change depending on the framework used?
The bundle size increases only if you import both at the same time, or when you have a default export + named exports, but just use the default export.
What i mean:
export default class {
...
}
export const a = '1';
----
Import Something from './myfile'
vs
export default class {
...
}
export const a = '1';
----
Import { a } from './myfile'
Method 2 has lower bundle size.
No, it does not depend on the framework, it is a "language feature".
A vue application I am working on currently has lots of code redundancies relating to date functions. In an effort to reduce these redundancies, I'd like to create a utility class as shown below, import it and set it to a Vue data property within the component, so I can call the date functions within it.
I am not certain on the best way to implement this. The current implementation results in an error saying TypeError: this.dates is undefined and my goal is not only to resolve this error but create/utilize the class in the Vue environment using best standards.
Importing utility class
import Dates from "./utility/Dates";
...
Component
const contactEditView = Vue.component('contact-edit-view', {
data() {
return {
contact: this.myContact
dates: Dates
}
},
...
Dates.js
export default {
dateSmall(date) {
return moment(date).format('L');
},
dateMedium(date) {
return moment(date).format('lll');
},
dateLarge(date) {
return moment(date).format('LLL');
}
};
View
Date of Birth: {{ dates.dateMedium(contact.dob) }}
My suggestion for this is to use a plugin option in Vue. About Vue plugin
So you will crate a new folder called services, add file yourCustomDateFormater.js:
const dateFormater = {}
dateFormater.install = function (Vue, options) {
Vue.prototype.$dateSmall = (value) => {
return moment(date).format('L')
}
Vue.prototype.$dateMedium = (value) => {
return moment(date).format('lll')
}
}
In main.js:
import YourCustomDateFormater from './services/yourCustomDateFormater'
Vue.use(YourCustomDateFormater)
And you can use it anywhere, like this:
this.$dateSmall(yourValue)
Or, if you want to use mixin. Read more about mixin
Create a new file dateFormater.js
export default {
methods: {
callMethod () {
console.log('my method')
}
}
}
Your component:
import dateFormater from '../services/dateFormater'
export default {
mixins: [dateFormater],
mounted () {
this.callMethod() // Call your function
}
}
Note: "Use global mixins sparsely and carefully, because it affects every single Vue instance created, including third party components. In most cases, you should only use it for custom option handling like demonstrated in the example above. It’s also a good idea to ship them as Plugins to avoid duplicate application." - Vue documentation
dateUtilsjs
import moment from 'moment-timezone'
function formatDateTime(date) {
return moment.utc(date).format("M/D/yyyy h:mm A")
}
export { formatDateTime }
Component JS
...
import { formatDateTime } from '../utils/dateUtils'
...
methods: {
formatDateTime,
}
Used within component
{{ formatDateTime(date) }}